2011年6月7日火曜日

Retrieving JSON Data



 JSONデータの取得


この時点でクライアントサイドのコードで株価データをシミュレートする
ストックウォッチャー(株価監視)アプリケーションの最初の実装を作成済です。

このページでは、代わりにJSON形式の株価データを取得する為にローカルサーバを呼び出します:
更なる情報は次の場所にあります:
  1. More about JSON, JSNI, Overlay types, and HTTP
Note: GWTアプリケーションでのクライアント-サーバ間通信のより広いガイドについては
Communicate with a Serverを見てください。

 始める前に


StockWatcher プロジェクト
このチュートリアルはGWTの概念とBuild a Sample GWT Application チュートリアルで作成した
StockWatcherアプリケーションを基にしています。 もしGWTアプリケーション作成チュートリアルを
終えておらずGWTの概念がある程度わかっているなら(慣れ親しんでいるなら)、
ここでStockWatcherプロジェクトをコードとしてインポートすることが出来ます。 
  1. StockWatcher projectをダウンロードします。
  2. ファイルを解凍します。
  3. Eclipse へプロジェクトをインポートします。
    1. ファイル メニューから、インポートメニューオプションを選択します。
    2. [一般]-[既存プロジェクトをワークスペースへ]を選択します。次へ(N)ボタンをクリックします。
    3. [ルート・ディレクトリーの選択(T)]でブラウズしStockWatcherのあるディレクトリ
      (ファイルを解凍した場所から)を選択します。完了(F) ボタンをクリックします。
antを使用している場合は、StockWatcher/build.xmlの何処にGWTを解凍したかを指す
 gwt.sdk プロパティを編集します

JSONとは何か?
JSONは世界共通の言語に依存しないデータフォーマット(形式)です。この方法はXMLに似ています。
XMLがタグを使うのに対して、JSONはJavaScriptのオブジェクトリテラルの表記法に基づきます。
( { ' 要素名1 ' : 値1 , ' 要素名2 ' : 値2 }という形式 ) 従ってフォーマットはXMLよりも単純です。一般的に,
JSONでエンコードされデータはXMLにおける同等のデータよりも冗長ではないので、JSONデータは
XMLデータよりも速くダウンロードします。
JSONフォーマットでStockWatcherの株価データをエンコードした場合、このような感じになります。
(ホワイトスペースは取り除かれますが)
[
  {
    "symbol": "ABC",
    "price": 87.86,
    "change": -0.41
  },
  {
    "symbol": "DEF",
    "price": 62.79,
    "change": 0.49
  },
  {
    "symbol": "GHI",
    "price": 67.64,
    "change": 0.05
  }
]

1. ローカルサーバ上でのJSONデータソースの作成

既存実装のレビュー

最初のStockWatcherの実装では、StockPriceクラスを作成しランダムな株価データを生成するために
refreshWatchListメソッドを使用し、それからStockWatcherのフレックステーブルに値を入れるために
updateTableメソッドを呼び出しました。
/**
   * Generate random stock prices.
   */
  private void refreshWatchList() {
    final double MAX_PRICE = 100.0; // $100.00
    final double MAX_PRICE_CHANGE = 0.02; // +/- 2%

    StockPrice[] prices = new StockPrice[stocks.size()];
    for (int i = 0; i < stocks.size(); i++) {
      double price = Random.nextDouble() * MAX_PRICE;
      double change = price * MAX_PRICE_CHANGE
          * (Random.nextDouble() * 2.0 - 1.0);

      prices[i] = new StockPrice(stocks.get(i), price, change);
    }

    updateTable(prices);
  }
このチュートリアルでは、JSONフォーマットの株価データを生成するためのサーブレットを作成します。
それからサーバからJSONデータを取得するためのHTTP呼び出しを作成します。
クライアントサイドのコードを書いている間、JSONデータで動かすためにJSNIとGWTオーバーレイタイプ(型)を使用します。

※以下のページを参考にしてまとめるとGWTオーバーレイタイプとはJavaでクライアントサイドのコーディングしている時の
JavaScriptObjectをラップする抽象クラスみたいなもの?



servletを書く
JSONフォーマットで仮想的な株式相場を提供するためにサーブレットを作成します。データを提供するための
組み込みサーブレットコンテナ(Jetty)を使用するために、JsonStockDataクラスをStockWatcherプロジェクトの
サーバディレクトリに追加し、webアプリケーション配備記述子(web.xml)でサーブレットへの参照を追加してください。 
Note: ローカルにインストールしたwebサーバ(Apache, IIS, etc)とPHPがある場合、
代わりに株価データを生成するPHPを書いて、ローカルサーバへ呼び出すことも出来ます。
この例で重要なことは株価データがJSONエンコードであり、サーバはローカルであるということです。
(※ここでの操作指示は開始時にcom.google.gwt.sample.stockwatcher.clientが選択状態であり、
com.google.gwt.sample.stockwatcher.serverは存在していない前提のようです。)

  1. サーブレットの作成
    パッケージエクスプローラ内でクライアントパッケージを選択します:
    com.google.gwt.sample.stockwatcher.client
    エクリプスでは、新規Javaクラスウィザードを起動します。 (ファイル(F) > 新規(N) > クラス)
  2. パッケージ(K)で、.client から .serverへ名前を変更します。
    名前に(M)にJsonStockDataと入力します。
    エクリプスは(実際のクラス作成時に同時に)サーバサイドコードとJsonStockDataクラスのスタブ用のパッケージを作成します。

  3. スタブコードを以下のコードに置き換えます。
    package com.google.gwt.sample.stockwatcher.server;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.util.Random;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class JsonStockData extends HttpServlet {
    
      private static final double MAX_PRICE = 100.0; // $100.00
      private static final double MAX_PRICE_CHANGE = 0.02; // +/- 2%
    
      @Override
      protected void doGet(HttpServletRequest req, HttpServletResponse resp)
          throws ServletException, IOException {
    
        Random rnd = new Random();
    
        PrintWriter out = resp.getWriter();
        out.println('[');
        String[] stockSymbols = req.getParameter("q").split(" ");
        for (String stockSymbol : stockSymbols) {
    
          double price = rnd.nextDouble() * MAX_PRICE;
          double change = price * MAX_PRICE_CHANGE * (rnd.nextDouble() * 2f - 1f);
    
          out.println("  {");
          out.print("    \"symbol\": \"");
          out.print(stockSymbol);
          out.println("\",");
          out.print("    \"price\": ");
          out.print(price);
          out.println(',');
          out.print("    \"change\": ");
          out.println(change);
          out.println("  },");
        }
        out.println(']');
        out.flush();
      }
    
    }
    

サーバーサイドコードをGWTモジュールに追加する

組み込みサーブレットコンテナ(Jetty)はJSONフォーマットで株価データを生成するサーブレットをホストすることが出来ます。
この設定をする為に配備記述子(web.xml)に<servlet>と<servlet-mapping>の要素を追加しJsonStockDataを指定します。
GWT 1.6で始める場合、サーブレットはGWTモジュール(StockWatcher.gwt.xml)の代わりに
配備記述子(web.xml)で定義されるべきです。
<servlet-mapping>要素内で、URLパターンは絶対ディレクトリパスの形式が可能です。
(例えば/spellcheck または /common/login)
サービスインターフェイス上で@RemoteServiceRelativePathアノテーションを伴うデフォルトサービスパスを指定した
(StockPriceServiceで指定した)場合、url-patternがアノテーションの値と一致するか確かめます。
StockPriceServiceを"stockPrices"へマッピングし、StockWatcher.gwt.xml内の<module>要素のrename-to属性が
"stockwatcher"なので、完全なURLは以下のようになります: 
http://localhost:8888/stockwatcher/stockPrices
  1. 配備記述子を編集します。(StockWatcher/war/WEB-INF/web.xml)
    greetServletはもはや必要がないので定義を取り除いても大丈夫です。
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE web-app
        PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd">
    
    <web-app>
    
      <!-- Default page to serve -->
      <welcome-file-list>
        <welcome-file>StockWatcher.html</welcome-file>
      </welcome-file-list>
    
      <!-- Servlets -->
     <servlet>
        <servlet-name>jsonStockData</servlet-name>
        <servlet-class>com.google.gwt.sample.stockwatcher.server.JsonStockData</servlet-class>
      </servlet>
    
      <servlet-mapping>
        <servlet-name>jsonStockData</servlet-name>
        <url-pattern>/stockwatcher/stockPrices</url-pattern>
      </servlet-mapping>
    
    </web-app>
    

サーバからJSONデータを取得出来るかどうかをテストします

  1. 開発モードでStockWatcherをデバッグします。
    この時点で株価データは依然としてクライアントサイドコードから来ています。
  2. 株価相場サーバをテストします。
    開発モードコードサーバが稼働状態にしてから、サーブレットURLへ株コードを渡します。
    http://localhost:8888/stockwatcher/stockPrices?q=ABC+DEF
  3. サーブレットはJSONフォーマットでエンコードされた模擬株価データの配列を生成します。
    JSON formatted stock data

2. クライアントサイドコードでのJSONデータの処理

概要

この時点で、あなたはサーバからJSONデータを取得出来ることを確認しています。
このセクションの続きで、サーバへのHTTP GETリクエストをコード化します。
最初に、クライアントサイドコードへ返されるJSONエンコードされたテキストで働くことに集中してください?
あなたの使用する二つのテクニックはJSNI(JavaScript Native Interface)とGWTオーバーレイタイプです。
これがサーバから返って来たJSONデータです。
[
  {
    "symbol": "ABC",
    "price": 47.65563005127077,
    "change": -0.4426563818062567
  },
]
最初に、JSON文字列をJavaScriptオブジェクトへ変換するためにJavaScript eval() 関数を使用します。
private final native JsArray<StockData> asArrayOfStockData(String json) /*-{
    return eval(json);
  }-*/;
それから、それらのオブジェクトへアクセスするためのメソッドを書くことが出来ます。
// JSNI methods to get stock data.
  public final native String getSymbol() /*-{ return this.symbol; }-*/;
  public final native double getPrice() /*-{ return this.price; }-*/;
  public final native double getChange() /*-{ return this.change; }-*/;
どちらの場合もJSNIを使用します。クライアントサイドコードがJavaScriptへコンパイルされた場合、
トークン内部に書いたようにJavaメソッドは正確にJavaScriptへ置き換えられます。

JSNIのコードディング
上の例からわかる通り、GWTモジュール内から手書き(GWT生成のものと対比されるような)の
JavaScriptメソッドを使用することが出来ます。

JSNIメソッドはnative宣言され、引数リストの最後から後端のセミコロンの間の特別な
フォーマットのコメント内にJavaScriptコードを含みます。

JSNIコメントブロックは正確なトークン /*-{ で始まり、正確なトークン }-*/ で終わります。 
JSNIメソッドはまさしく普通のJavaメソッドのように呼び出されます。
スタティックメソッドまたはインスタンスメソッドになることが出来ます。
詳細: あなたのJavaソースコードへの手書きのJavaScriptの混入についてのTips、トリック、警告は
開発者ガイド JavaScript Native Interface (JSNI)を見てください。

JSONのJavaScriptオブジェクトへの変換

最初に、サーバからのJSONテキストをJavaScriptオブジェクトへ変換する必要があります。
これを行う、最も簡単で速い方法はJavaScript内蔵のeval()関数を使うことです。
eval()関数は有効なJSONテキストを正確に解析し、適切なオブジェクトを産み出します。
しかしながら、eval() はどんなJavaScriptコード(JSONデータではない)も実行出来るので、このアプローチは若干深刻な
セキュリティの問題(セキュリティへの影響?)があります。あなたが対話するサーバはあなたのアプリケーション内で任意の
JavaScriptコードを実行出来る能力を持ってしまう為、そのサーバが完全に信頼出来るか確かめてください。 この例では、
データにアクセスするために自分自身のマシン上のサーブレットコンテナを使用しているので、この問題は起きません。

  1. 文字列をJavaScript配列へ変換します。
    StockWatcherクラスで、次のメソッドを追加します。
      /**
       * Convert the string of JSON into JavaScript object.
       */
      private final native JsArray<StockData> asArrayOfStockData(String json) /*-{
        return eval(json);
      }-*/;
    
    エクリプスはJsArryとStockDataにフラグをつけます。
    StockDataはStockPriceクラスと置き換えるために使用するオーバーレイタイプです。

  2. import宣言します。
    import com.google.gwt.core.client.JsArray;
  3. クライアントパッケージでStockDataクラス用のスタブコードを作成します。
    StockWatcherクラス内に残っているコンパイルエラーは全て無視してください。
    StockDataクラスをコード化した後にこれは解決されます。

JSON データ型

あなたの予想通り、JSONデータの型はJavaScriptに組み込まれているものと対応しています。
JSONは文字列、数列、論理値、null値及びオブジェクトとこれらの型で構成された配列をエンコード出来ます。
JavaScriptにおいてはオブジェクトは順序不動の名前/値の組のセットです。
しかしながら、JSONオブジェクトでは値は他のJSON型のみが成り得ます。
(決して実行可能なJavaScriptコードを含む関数ではありません?)
JSON文字列を何かに変換することが出来る別の方法は静的なJSONParser.parse(String)メソッドを使うことです。
GWTはcom.google.gwt.json.client パッケージ内にJSONデータを操作する為のJSON型のフルセットを含んでいます。
JSONデータを解析することを選ぶならば、開発者ガイド, Working with JSONを見てください。
最終的に両方の方法はJavaScriptのeval()関数に依存します;
従って依然として信頼されたJSONデータのソースを使用していることを保証する責任があります。

オーバーレイタイプの作成

あなたの次のタスクはStockPrice型(クラス)をStockData型(クラス)に置き換えることです。
あなたはJSONオブジェクトの配列にアクセスするだけでなく、コーディング中は
JSONオブジェクトがJavaオブジェクトとして機能することを望むでしょう?
GWT オーバーレイ 型 はこれを可能にします。
新しいStockDataクラスは既存のJavaScript配列をオーバーレイするオーバーレイタイプになります。
  1. スタブコードを以下のコードに置換えます。
    Note: コメントナンバーは以下のインプリメンテーションノートを参照しています。これらは削除しても構いません。
    package com.google.gwt.sample.stockwatcher.client;
    
    import com.google.gwt.core.client.JavaScriptObject;
    
    class StockData extends JavaScriptObject {                              // (1)
      // Overlay types always have protected, zero argument constructors.
      protected StockData() {}                                              // (2)
    
      // JSNI methods to get stock data.
      public final native String getSymbol() /*-{ return this.symbol; }-*/; // (3)
      public final native double getPrice() /*-{ return this.price; }-*/;
      public final native double getChange() /*-{ return this.change; }-*/;
    
      // Non-JSNI method to return change percentage.                       // (4)
      public final double getChangePercent() {
        return 100.0 * getChange() / getPrice();
      }
    }
    

インプリメンテーションノート

(1) StockData はJavaScriptオブジェクトのサブクラスです。
(GWTがJavaScriptオブジェクトを示すために使用するマーカータイプ)
JavaScriptオブジェクトはGWTコンパイラと開発モードコードサーバから特別な扱いを受けます。
その目的はネイティブJavaScriptオブジェクトのJavaコードへの不明瞭な表現を提供することです。

(2)オーバーレイタイプは常にProtectedで引数なしのコンストラクタを持ちます。

(3) オーバーレイタイプでの典型的なメソッドはJSNIです。
これらのgetterメソッドはあなたが存在していることを知っているJSONフィールドに直接アクセスします。
設計では、オーバーレイタイプのすべてのメソッドはfinalでprivateです;したがって、あらゆるメソッドは
コンパイラによって静的に解決出来るのでランタイムで動的なディスパッチが必要ありません。

(4) しかしながら、 オーバーレイタイプでのメソッドはJSNIであることが必須ではありません。
StockPriceクラスでちょうどしたように、価格と変動値に基づいた変動率を計算します。
オーバーレイタイプ使用による利点
オーバーレイタイプの使用は、コード補完、リファクタリング、コンパイル時チェックを使用して
対話出来る通常のJavaタイプ(型)を作成します。
にも関わらず、さらに任意のJavaScriptと対話する柔軟性を持ちます。
(RequestBuilder(あなたが次のセクションで作成する)を使用してJSONサービスへのアクセスをより簡単にします?)  

GWTは現在、StockDataのどのインスタンスもこのGWTモジュールの外側から来る正真正銘の
JavaScriptオブジェクトであるということを理解します。

あなたはそれがJavaScript内に存在するかのように正確に対話することが出来ます。
この例では、あなたが存在を知っているJSONフィールドに直接アクセス出来ます:
 this.Price と this.Change.
オーバーレイタイプでのメソッドはGWTコンパイラによって静的に解決することが出来るので、
自動的なインライン化の候補になります。インライン化されたコードはかなり速く動きます。

これはGWTコンパイラがアプリケーションのクライアント側のコードとして非常に最適化されたJavaScriptを作成することを可能にします。

3. サーバからデータ取得するためのHTTPリクエストの作成

今、あなたは適切にJSONデータを伴って機能するメカニズムを持っています。
サーバからデータを取得するHTTPリクエストを書きましょう。

この例では、あなたは現在のrefreshWatchListメソッドとHTTPを使用する新しい実装と置換します。

URLの指定

初めに、サーブレットが動いているURLを指定します、すなわち:
http://localhost:8888/stockwatcher/stockPrices?q=ABC+DFD
Note: phpの例をやっている場合は適切なURLを代用してください。
それからベースモジュールURLへ監視リスト内のストックコードを付加してください。

JSONサーバのURLをハードコーディング(決め打ち)するよりもStockWatcherクラスへ定数を追加します。
  1. JSONデータのURLを指定する定数をStockWatcherクラスへ追加します。
    Note: 先にモジュールXMLファイル内で/stockPricesへのパスを指定します。
    private static final String JSON_URL = GWT.getModuleBaseURL() + "stockPrices?q=";
    エクリプスはGWTにフラグをつけます。
  2. import宣言します。
    import com.google.gwt.core.client.GWT;
  3. クエリURLへ株価コードを付加し、エンコードします。
    既存のrefreshWatchListを以下のコードに置換します。
    private void refreshWatchList() {
        if (stocks.size() == 0) {
          return;
        }
    
        String url = JSON_URL;
    
        // Append watch list stock symbols to query URL.
        Iterator iter = stocks.iterator();
        while (iter.hasNext()) {
          url += iter.next();
          if (iter.hasNext()) {
            url += "+";
          }
        }
    
        url = URL.encode(url);
    
        // TODO Send request to server and handle errors.
    
      }
    エクリプスはイテレータとURLにフラグをつけます。
  4. import宣言します。
    import com.google.gwt.http.client.URL;
    
    import java.util.Iterator;

 非同期HTTP

サーバからのJSONテキストを取得するには、com.google.gwt.http.clientパッケージ内のHTTPクライアントクラスを使用します。
これらのクラスは非同期HTTPリクエストを作成する機能性を包含しています。

StockWatcherが継承する必要があるHTTPタイプは別々のGWTモジュール内に含まれています。 

  1. GWTモジュールを継承するには、モジュールXMLファイルを編集します。
    StockWatcher.gwt.xml内で、<inherits>タグを追加し、HTTPモジュールを指定します。
    <!-- Other module inherits -->
      <inherits name="com.google.gwt.http.HTTP" />
            
  2. StockWatcher.javaを開き、以下のimport宣言を追加します。以下のJavaタイプのimportを宣言します。
    import com.google.gwt.http.client.Request;
    import com.google.gwt.http.client.RequestBuilder;
    import com.google.gwt.http.client.RequestCallback;
    import com.google.gwt.http.client.RequestException;
    import com.google.gwt.http.client.Response;

 カスタムHTTPリクエストの構築

リクエストを送信するには、RequestBuilderオブジェクトのインスタンスを作成します。
コンストラクタでHTTPメソッド(GET、POST、その他)とURLを指定します。
必要ならば、HTTPリクエストで使用するユーザーネーム、パスワード、タイムアウト、ヘッダをセットすることも出来ます。
この例ではその必要はありません。
リクエスト作成の準備が出来たら、sendRequest(String, RequestCallback)を呼び出します。
あなたが渡したRequestCallback引数は、そのonResponseReceived(Request, Response)メソッドで
レスポンスを処理します。このメソッドはもしHTTPコールが成功裡に完了した場合、その時呼び出されます。
呼び出しが失敗(例えば、HTTPサーバが応答しない)した場合、代わりにonError(Request, Throwable)メソッドが
呼び出されます。RequestCallbackインターフェイスはGWTリモートプロシージャコールにおける
AsyncCallbackインターフェイスに似ています。
  1. HTTPリクエストを作成し、JSONレスポンスを解析します。
    refreshWatchListメソッド内でTODOコメントと以下のコードを置換します。
    // Send request to server and catch any errors.
        RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, url);
    
        try {
          Request request = builder.sendRequest(null, new RequestCallback() {
            public void onError(Request request, Throwable exception) {
              displayError("Couldn't retrieve JSON");
            }
    
            public void onResponseReceived(Request request, Response response) {
              if (200 == response.getStatusCode()) {
                updateTable(asArrayOfStockData(response.getText()));
              } else {
                displayError("Couldn't retrieve JSON (" + response.getStatusText()
                    + ")");
              }
            }
          });
        } catch (RequestException e) {
          displayError("Couldn't retrieve JSON");
        }
  2. すぐに解決出来る二つのコンパイルエラーを受け取るでしょう。

 updateTableメソッドの修正

コンパイルエラーを修正するには、updateTableメソッドを修正します。



  • 変更: StockPrice[] prices -> JsArray<StockData> prices





  • 変更: prices.length    -> prices.length()





  • 変更: prices[i]      ー> prices.get(i)





    1. 既存のupdateTable(StockPrice[])メソッドを以下のコードに置換します。(※updateは余計?)
      /**
         * Update the Price and Change fields for all rows in the stock table.
         *
         * @param prices Stock data for all rows.
         */
        private void updateTable(JsArray<StockData> prices) {
          for (int i = 0; i < prices.length(); i++) {
            updateTable(prices.get(i));
          }
      
          // Display timestamp showing last refresh.
          lastUpdatedLabel.setText("Last update : "
              + DateTimeFormat.getMediumDateTimeFormat().format(new Date()));
        }
    2. updateTable(StockPrice price)で対応する変更をします。
      StockPriceクラスへの参照をStockDataクラスへと変更します。
      private void updateTable(StockData price) {
          // Make sure the stock is still in the stock table.
          if (!stocks.contains(price.getSymbol())) {
            return;
          }
      
          ...
      
        }
      

     4. GETエラーの処理

    途中で何かが発生した場合 (例えば、サーバがオフラインまたはJSONがフォーマット異常の場合),
    エラーをトラップ(捕捉)してユーザーにメッセージを表示するでしょう。
    これを行うには、レーベルウィジェットを作成し、新しいメソッド displayError(String)を書きます。
    1. StockWatcherクラス内でスタブメソッドを作成することによってコンパイルエラーを消去します。

      エラーメッセージ用のテキストをセットし、レーベルウィジェットを可視状態にします。
      /**
         * If can't get JSON, display error message.
         * @param error
         */
        private void displayError(String error) {
          errorMsgLabel.setText("Error: " + error);
          errorMsgLabel.setVisible(true);
        }
      
    2. エクリプスはerrorMsgLabelにフラグをつけます。
      しばらくの間、コンパイルエラーを無視します;
      次のステップでエラーテキストを表示するためのレーベルウィジェットを実装します。
    3. エラーが訂正された場合、レーベルウィジェットを隠します。
      updateTable(JsArray<StockData> prices) メソッド内ですべてのエラーメッセージを消去します。
      private void updateTable(JsArray<StockData> prices) {
          for (int i=0; i < prices.length; i++) {
            updateTable(prices[i]);
          }
      
          // Display timestamp showing last refresh.
          lastUpdatedLabel.setText("Last update : " +
              DateTimeFormat.getMediumDateTimeFormat().format(new Date()));
      
          // Clear any errors.
          errorMsgLabel.setVisible(false);
        }
      

    エラーメッセージの表示

    エラーを表示するために、新しいUIコンポーネントが必要です; レーベルウィジェットを実装しましょう。
    1. ユーザーの注意をひきつけるようにエラーメッセージ用のスタイルを定義します。
      StockWatcher.cssで、.errorMessage クラス属性を伴う全ての要素に適用するスタイルルールを作成します。
      .negativeChange {
        color: red;
      }
      
      .errorMessage {
        color: red;
      }
    2. エラーメッセージのテキストを保持するには、レーベルウィジェットに追加します。
      StockWatcher.javaで以下のインスタンスフィールドを追加します。
      private ArrayList<String> stocks = new ArrayList<String>();
        private Label errorMsgLabel = new Label();
    3. StockWatcher起動時にerrorMsgLabelを初期化します。
      onModuleLoadメソッドで、StockWatcherロード時にerrorMsgLabelへセカンダリクラス属性を追加し、
      表示しないようにします。 

      メインパネルのstocksFlexTableの上にerrorMsgLabelを追加します。
      // Assemble Add Stock panel.
          addPanel.add(newSymbolTextBox);
          addPanel.add(addButton);
          addPanel.addStyleName("addPanel");
      
          // Assemble Main panel.
          errorMsgLabel.setStyleName("errorMessage");
          errorMsgLabel.setVisible(false);
      
          mainPanel.add(errorMsgLabel);
          mainPanel.add(stocksFlexTable);
          mainPanel.add(addPanel);
          mainPanel.add(lastUpdatedLabel);
      

    HTTPリクエストとエラー処理のテスト

    1. 開発モードでStockWatcherを更新(リフレッシュ)します。
    2. 株コードをいくつか入力します。
      StockWatcherは各株の株価と変動データを表示するはずです。
      株データはプログラムから生成されたものではなく、ローカルサーバからJSONフォーマットで来ます。

    3. 無効なURLを呼び出すことにより、HTTPエラーメッセージをテストします。
      StockWatcher,javaでURLを編集します。
      変更: private static final String JSON_URL = GWT.getModuleBaseURL() + "stockPrices?q=";
      -> private static final String JSON_URL = GWT.getModuleBaseURL() + "BADURL?q=";
    4. 開発モードでStockWatcherを更新します。
    5. 株コードを入力します。
      StockWatcherは赤字のエラーメッセージを表示します。. 有効な株コードデータの更新は停止されます。
      screenshot: JSON error message
    6. StockWatcher,javaで、URLを訂正します。
    7. 開発モードでStockWatcherを更新します。
      株コードをいくつか入力します。
      StockWatcherは再び、各株の株価と変動データを表示するはずです。

     JSON、 JSNI、オーバーレイタイプ、HTTPについての詳細

    この時点で、あなたはローカルサーバからJSONエンコードされた株データを取得し、
    それを使用して監視リストの株のPrice/changeフィールドを更新しています。
    もし別のドメインのwebサーバからJSONを取得する方法が知りたければ、Making cross-site requestsを見てください。
    クライアント−サーバ間通信についてより詳しく学ぶには開発者ガイドのサーバとの通信を見てください。
    トピックが含まれているのは:
    JSNIについてより詳しく学ぶには、開発者ガイドのJavaScript Native Interface (JSNI)を見てください。
    トピックが含まれているのは:
    • Writing Native JavaScript Method
    • Accessing Java Methods and Fields from JavaScript
    • Sharing objects between Java source and JavaScript
    • Exceptions and JSNI
    • JavaScript Overlay Types