Locked History Actions

Diff for "GWT/DevGuideCodingBasicsDeferred"

Differences between revisions 1 and 2
Deletions are marked like this. Additions are marked like this.
Line 18: Line 18:
 *   Reduces the size of the generated JavaScript code that a client will need to download by only including the code needed to run a particular browser/locale instance (used by the Internationalization module)
 *    Saves development time by automatically generating code to implement an interface or create a proxy class (used by the GWT RPC module)
 *    Since the implementations are pre-bound at compile time, there is no run-time penalty to look up an implementation in a data structure as with dynamic binding or using virtual functions.
 * Reduces the size of the generated JavaScript code that a client will need to download by only including the code needed to run a particular browser/locale instance (used by the Internationalization module)
 * インターフェース実装やプロキシクラスを自動的にコード生成することにより、開発時間を短縮する(これはGWT RPCモジュールで使用されている)。
 * 実装はコンパイル時にpre-boundされているため、動的バインディングや仮想関数のような実行時のlook upペナルティが存在しない。
Line 22: Line 22:
Some parts of the toolkit make implicit use of deferred binding, that is, they use the technique as a part of their implementation, but it is not visible to the user of the API. For example, many widgets and panels as well as the DOM class use this technique to implement browser specific logic. Other GWT features require the API user to explicity invoke deferred binding by designing classes that follow specific rules and instantiating instances of the classes with GWT.create(Class), including ツールキットの幾つかの部分は、遅延バインディングを暗黙的に使用している。
つまり、ツールキットは自身の実装の一部としてその技術を利用しているのだが、APIのユーザから見えない。
例えば、DOMクラスもそうだが、たくさんのウィジェットやパネルはこのテクニックをブラウザ特有のロジックの実装に使用している。
Line 24: Line 26:
== GWT RPC and I18N. == Other GWT features require the API user to explicity invoke deferred binding by designing classes that follow specific rules and instantiating instances of the classes with GWT.create(Class), including GWT RPC and I18N.
Line 30: Line 32:
== Defining Deferred Binding Rules == == 遅延バインディングルールを定義する ==
Line 32: Line 34:
There are two ways in which types can be replaced via deferred binding: タイプを遅延バインディングによって置換する方法としては二つある。
Line 34: Line 36:
    Replacement: A type is replaced with another depending on a set of configurable rules.
    Code generation: A type is substituted by the result of invoking a code genreator at compile time.
 * 置換:タイプは、構成可能なルールにのっとり、他のタイプに置換される。
 * コード生成:コンパイル時のコードジェネレータの起動の結果作成されたタイプによって置換される。
Line 37: Line 39:
Directives in Module XML files == モジュールXMLファイル中のディレクティブ ==
Line 39: Line 41:
The deferred binding mechanism is completely configurable and does not require editing the GWT distributed source code. Deferred binding is configured through the <replace-with> and <generate-with> elements in the module XML files. The deferred binding rules are pulled into the module build through <inherits> elements. 遅延バインディングメカニズムは完全に構成可能であり、GWTで配布されたソースコードを変更する必要は全くない。
遅延バインディングはモジュールXMLファイル中の<replace-with>と<generate-with>によって構成される。
The deferred binding rules are pulled into the module build through <inherits> elements.
Line 41: Line 45:
For example, the following configuration invokes deferred binding for the PopupPanel widget: 例えば、以下のコンフィギュレーションはPOpupPanelウィジェットの遅延バインディングを起動する。
Line 48: Line 52:
Deferred Binding Using Replacement
==
Deferred Binding Using Replacement ==
Line 52: Line 57:
{{{
Line 76: Line 82:
}}}
Line 81: Line 88:
{{{
Line 83: Line 90:
}}}

Coding Basics - Deferred Binding

遅延バインディングはGWTコンパイラの機能である。 これは、コンパイル時に多くのバージョンを作成し、ランタイム時には対象となるクライアント向けに、ブートストラッピング時にその中の一つのコードのみをロードするというものである。 それぞれのバージョンはブラウザベースではあるが、それに加え、ユーザのアプリケーション自身の定義や使用するものに依存させることができる。

例えば、GWTの国際化モジュールを使ってアプリを国際化したいという場合、GWTコンパイラはブラウザ環境毎のバージョンを生成することができる。例えば、"Firefox in English", "Firefox in French", "Internet Explorer in English"という具合だ。その結果、特定のブラウザ環境に必要なコードのみを手作業で作成したjavascriptに比べて、コンパクトで素早くダウンロード可能なjavascriptコードをデプロイすることができる。

遅延バインディングのメリット

遅延バインディングはGWTコンパイラで使用されている技術であり、これはパラメータセットを元とするクラス実装を生成し、選択するものである。キモとしては、遅延バインディングとは、Javaリフレクションに対するGWTの答えである。 これによって、GWT開発者はアプリのブラウザ環境毎に異なったバリエーションを生成することができ、実際のブラウザ上での実行時にただ一つだけをダウンロードさせることができる。

遅延バインディングは様々なメリットを持つ。

  • Reduces the size of the generated JavaScript code that a client will need to download by only including the code needed to run a particular browser/locale instance (used by the Internationalization module)

  • インターフェース実装やプロキシクラスを自動的にコード生成することにより、開発時間を短縮する(これはGWT RPCモジュールで使用されている)。
  • 実装はコンパイル時にpre-boundされているため、動的バインディングや仮想関数のような実行時のlook upペナルティが存在しない。

ツールキットの幾つかの部分は、遅延バインディングを暗黙的に使用している。 つまり、ツールキットは自身の実装の一部としてその技術を利用しているのだが、APIのユーザから見えない。 例えば、DOMクラスもそうだが、たくさんのウィジェットやパネルはこのテクニックをブラウザ特有のロジックの実装に使用している。

Other GWT features require the API user to explicity invoke deferred binding by designing classes that follow specific rules and instantiating instances of the classes with GWT.create(Class), including GWT RPC and I18N.

As a user of the Google Web Toolkit, you may never need to create a new interface that uses deferred binding. If you follow the instructions in the guide for creating internationalized applications or GWT RPC calls you will be using deferred binding, but you will not have to actually write any browser dependent or locale dependent code.

The rest of the deferred binding section describes how to create new rules and classes using deferred binding. If you are new to the toolkit or only intend to use pre-packaged widgets, you will probably want to skip on to the next topic. If you are interested in programming entirely new widgets from the ground up or other functionality that requires cross-browser dependent code, the next sections should be of interest.

遅延バインディングルールを定義する

タイプを遅延バインディングによって置換する方法としては二つある。

  • 置換:タイプは、構成可能なルールにのっとり、他のタイプに置換される。
  • コード生成:コンパイル時のコードジェネレータの起動の結果作成されたタイプによって置換される。

モジュールXMLファイル中のディレクティブ

遅延バインディングメカニズムは完全に構成可能であり、GWTで配布されたソースコードを変更する必要は全くない。 遅延バインディングはモジュールXMLファイル中の<replace-with>と<generate-with>によって構成される。 The deferred binding rules are pulled into the module build through <inherits> elements.

例えば、以下のコンフィギュレーションはPOpupPanelウィジェットの遅延バインディングを起動する。

  • Top level <module>.gwt.xml inherits com.google.gwt.user.User com/google/gwt/user/User.gwt.xml inherits com.google.gwt.user.Popup com/google/gwt/user/Popup.gwt.xml contains <replace-with> elements to define deferred binding rules for the PopupPanel class.

Inside the PopupPanel module XML file, there happens to be some rules defined for deferred binding. In this case, we're using a replacement rule.

Deferred Binding Using Replacement

The first type of deferred binding uses replacement. Replacement means overriding the implementation of one java class with another that is determined at compile time. For example, this technique is used to conditionalize the implementation of some widgets, such as the PopupPanel. The use of <inherits> for the PopupPanel class is shown in the previous section describing the deferred binding rules. The actual replacement rules are specified in Popup.gwt.xml, as shown below:

<module>

  <!--  ... other configuration omitted ... -->

  <!-- Fall through to this rule is the browser isn't IE or Mozilla -->
  <replace-with class="com.google.gwt.user.client.ui.impl.PopupImpl">
    <when-type-is class="com.google.gwt.user.client.ui.impl.PopupImpl"/>
  </replace-with>

  <!-- Mozilla needs a different implementation due to issue #410 -->
  <replace-with class="com.google.gwt.user.client.ui.impl.PopupImplMozilla">
    <when-type-is class="com.google.gwt.user.client.ui.impl.PopupImpl" />
    <any>
      <when-property-is name="user.agent" value="gecko"/>
      <when-property-is name="user.agent" value="gecko1_8" />
    </any>
  </replace-with>

  <!-- IE has a completely different popup implementation -->
  <replace-with class="com.google.gwt.user.client.ui.impl.PopupImplIE6">
    <when-type-is class="com.google.gwt.user.client.ui.impl.PopupImpl"/>
    <when-property-is name="user.agent" value="ie6" />
  </replace-with>
</module>

These directives tell the GWT compiler to swap out the PoupImpl class code with different class implementations according to the the user.agent property. The Popup.gwt.xml file specifies a default implementation for the PopupImpl class, an overide for the Mozilla browser (PopupImplMozilla is substituted for PopupImpl), and an override for Internet Explorer version 6 (PopupImplIE6 is substituted for PopupImpl). Note that PopupImpl class or its derived classes cannot be instantiated directly. Instead, the PopupPanel class is used and the GWT.create(Class) technique is used under the hood to instruct the compiler to use deferred binding. Example Class Hierarchy using Replacement

To see how this is used when designing a widget, we will examine the case of the PopupPanel widget further. The PopupPanel class implements the user visible API and contains logic that is common to all browsers. It also instantiates the proper implementation specific logic using the GWT.create(Class) as follows:

  private static final PopupImpl impl = GWT.create(PopupImpl.class);

The two classes PopupImplMozilla and PopupImplIE6 extend the PopupImpl class and override some PopupImpl's methods to implement browser specific behavior.

Then, when the PopupPanel class needs to switch to some browser dependent code, it accesses a member function inside the PopupImpl class:

  • public void setVisible(boolean visible) {
    • // ... common code for all implementations of PopupPanel ...

      // If the PopupImpl creates an iframe shim, it's also necessary to hide it // as well. impl.setVisible(getElement(), visible);

    }

The default implementation of PopupImpl.setVisible() is empty, but PopupImplIE6 has some special logic implemented as a JSNI method:

  • public native void setVisible(Element popup, boolean visible) /*-{
    • if (popup.frame) {

      • popup.frame.style.visibility = visible ? 'visible' : 'hidden';

      }
    }-*/;{

After the GWT compiler runs, it prunes out any unused code. If your application references the PopupPanel class, the compiler will create a separate JavaScript output file for each browser, each containing only one of the implementations: PopupImpl, PopupImplIE6 or PopupImplMozilla. This means that each browser only downloads the implementation it needs, thus reducing the size of the output JavaScript code and minimizing the time needed to download your application from the server. Deferred Binding using Generators

The second technique for deferred binding consists of using generators. Generators are classes that are invoked by the GWT compiler to generate a Java implementation of a class during compilation. When compiling for production mode, this generated implementation is directly translated to one of the versions of your application in JavaScript code that a client will download based on its browser environment.

The following is an example of how a deferred binding generator is specified to the compiler in the module XML file hierarchy for the RemoteService class - used for GWT-RPC:

  • Top level <module>.gwt.xml inherits com.google.gwt.user.User com/google/gwt/user/User.gwt.xml inherits com.googl.gwt.user.RemoteService com/google/gwt/user/RemoteService.gwt.xml contains <generates-with> elements to define deferred binding rules for the RemoteService class.

Generator Configuration in Module XML

The XML element <generate-with> tells the compiler to use a Generator class. Here are the contents of the RemoteService.gwt.xml file relevant to deferred binding:

<module>

  • <!-- ... other configuration omitted ... -->

    <!-- Default warning for non-static, final fields enabled --> <set-property name="gwt.suppressNonStaticFinalFieldWarnings" value="false" />

    <generate-with class="com.google.gwt.user.rebind.rpc.ServiceInterfaceProxyGenerator">

    • <when-type-assignable class="com.google.gwt.user.client.rpc.RemoteService" />

    </generate-with>

</module>

These directives instruct the GWT compiler to invoke methods in a Generator subclass (ServiceInterfaceProxyGenerator) in order to generate special code when the deferred binding mechanism GWT.create() is encountered while compiling. In this case, if the GWT.create() call references an instance of RemoteService or one of its subclasses, the ServiceInterfaceProxyGenerator's generate()` method will be invoked. Generator Implementation

Defining a subclass of the Generator class is much like defining a plug-in to the GWT compiler. The Generator gets called to generate a Java class definition before the Java to JavaScript conversion occurs. The implementation consists of one method that must output Java code to a file and return the name of the generated class as a string.

The following code shows the Generator that is responsible for deferred binding of a RemoteService interface:

/**

  • Generator for producing the asynchronous version of a
  • {@link com.google.gwt.user.client.rpc.RemoteService RemoteService} interface. */

public class ServiceInterfaceProxyGenerator extends Generator {

  • /**
    • Generate a default constructible subclass of the requested type. The
    • generator throws <code>UnableToCompleteException</code> if for any reason

    • it cannot provide a substitute class
    • @return the name of a subclass to substitute for the requested class, or
    • return <code>null</code> to cause the requested type itself to be

    • used
    • /

    public String generate(TreeLogger logger, GeneratorContext ctx,

    • TypeOracle typeOracle = ctx.getTypeOracle(); assert (typeOracle != null); JClassType remoteService = typeOracle.findType(requestedClass); if (remoteService == null) {

      } if (remoteService.isInterface() == null) { }

      ProxyCreator proxyCreator = new ProxyCreator(remoteService);

      TreeLogger proxyLogger = logger.branch(TreeLogger.DEBUG,

      • "Generating client proxy for remote service interface '"
        • + remoteService.getQualifiedSourceName() + "'", null);
      return proxyCreator.create(proxyLogger, ctx);
    }

}

The typeOracle is an object that contains information about the Java code that has already been parsed that the generator may need to consult. In this case, the generate() method checks it arguments and the passes off the bulk of the work to another class (ProxyCreator).