Locked History Actions

GWT/TIPS

TIPS

クライアント内のリソースをユーザにダウンロードさせる

ユーザに何かをダウンロードさせるには、URLを示してサーバからダウンロードさせるのが普通だが、 ここでは、クライアント内にあるリソースを(当然のことながらサーバを介さずに)ユーザにダウンロードさせてみる。 ここでは簡単なテキストをダウンロードさせる。

        String downloadText = "なんでもいい";
        String windowName = "test.txt";
        
        byte[]bytes;
        try {
          // なぜかutf-8ではだめ。これはバグとしてあがっている
          bytes = downloadText.getBytes("UTF-8");
        } catch (Exception ex) {
          ex.printStackTrace();
          return;
        }
        String base64 = new String(Base64Coder.encode(bytes));
        String dataUri = "data:application/octet-stream;base64," + base64;
        Window.open(dataUri, windowName, "");

Base64Coderhttp://www.source-code.biz/base64coder/java/を使う。ただし、この中の

  private static final String systemLineSeparator = System
      .getProperty("line.separator");

はGWTクライアント側で動作しないので、

  private static final String systemLineSeparator = "\n";

とでもしておく。 ちなみに、com.google.gwt.user.server.Base64Utilsというものがあるが、クライアントサイドで動作させる方法が不明。

以上でとりあえずダウンロードはできるが、ファイル名を指定する方法は不明。

マウスカーソル形状の変更

DialogBoxのoffsetWidthが必ず400になっている

自動生成されるcssになぜか

/** Most GWT widgets already have a style name defined */
.gwt-DialogBox {
  width: 400px;
}

という定義があるため。

任意のオブジェクトをRPCで送受信する

例えばGWTP使用の場合に、任意のオブジェクトをサーバ側に受け渡そうとすると、

public class SomethingPut implements UnsecuredActionImpl<PreferencePut.Response> {
  
  public Object value;
  
  @SuppressWarnings("unused") private PreferencePut() {}
  
  public PreferencePut(Object value) { 
    this.value = value;
  }

  ....
}

という風に記述してしまいそうだが、これはうまくいかない。 詳細は不明だが、GWTではコードのコンパイル時に、「どのオブジェクトがシリアライズされるべきか」がわかっていなければならないようだ。 しかし、そうかといって、

public class SomethingPut implements UnsecuredActionImpl<PreferencePut.Response> {
  
  public Serializable value;
  
  @SuppressWarnings("unused") private PreferencePut() {}
  
  public PreferencePut(Serializable value) { 
    this.value = value;
  }

  ....
}

としてしまうと、Serializableを継承するすべてのクラスについて直列化のための準備がされてしまう模様。 以下のようにするのが適切かと思われる(現在のところ)。つまり、

public interface MySerializable extends Serializable {}

と独自のマーカインターフェースを作成しておき、

public class SomethingPut implements UnsecuredActionImpl<PreferencePut.Response> {
  
  public MySerializable value;
  
  @SuppressWarnings("unused") private PreferencePut() {}
  
  public PreferencePut(MySerializable value) { 
    this.value = value;
  }

  ....
}

としてしまう。こうすると、このアクションを使って直列化されるべきクラスが明確になるようで、GWTはコンパイル時にその準備ができるらしい。

UnitCacheLoaderの問題

開発環境での起動時に以下のような例外が発生することがある。

Exception in thread "UnitCacheLoader" java.lang.RuntimeException: Unable to read from byte cache
        at com.google.gwt.dev.util.DiskCache.transferFromStream(DiskCache.java:166)
        at com.google.gwt.dev.util.DiskCacheToken.readObject(DiskCacheToken.java:87)
        at sun.reflect.GeneratedMethodAccessor3.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at java.io.ObjectStreamClass.invokeReadObject(Unknown Source)
        at java.io.ObjectInputStream.readSerialData(Unknown Source)
        at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
        at java.io.ObjectInputStream.readObject0(Unknown Source)
        at java.io.ObjectInputStream.defaultReadFields(Unknown Source)
        at java.io.ObjectInputStream.readSerialData(Unknown Source)
        at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
        at java.io.ObjectInputStream.readObject0(Unknown Source)
        at java.io.ObjectInputStream.readObject(Unknown Source)
        at com.google.gwt.dev.javac.PersistentUnitCache.loadUnitMap(PersistentUnitCache.java:493)
        at com.google.gwt.dev.javac.PersistentUnitCache.access$000(PersistentUnitCache.java:92)
        at com.google.gwt.dev.javac.PersistentUnitCache$UnitCacheMapLoader.run(PersistentUnitCache.java:122)
Caused by: java.io.StreamCorruptedException: unexpected EOF in middle of data block
        at java.io.ObjectInputStream$BlockDataInputStream.refill(Unknown Source)
        at java.io.ObjectInputStream$BlockDataInputStream.read(Unknown Source)
        at java.io.ObjectInputStream.read(Unknown Source)
        at java.io.InputStream.read(Unknown Source)
        at com.google.gwt.dev.util.DiskCache.transferFromStream(DiskCache.java:154)
        ... 16 more

開発環境中のgwt-unitCacheを削除すればよい。

IE9でDialogBox+DockLayoutPanelの表示が壊れる

以下に記述のある通り。

以下のプログラムでcreateit(),showit()を連続的に呼び出せば問題無いが、あらかじめcreateit()を呼び出しておき、何かのボタンが押された時にshowitを呼び出すと、表示がおかしい(north,southが表示されない)。

  DialogBox dialog;

  private void createit() {
    DockLayoutPanel root = new DockLayoutPanel(Unit.EM);
    root.addNorth(new HTML("north"), 2);
    root.addSouth(new HTML("south"), 2);
    root.add(new HTML("center"));
    root.setSize("700px", "300px");
    dialog = new DialogBox();
    dialog.add(root);
    dialog.setModal(true);
    dialog.setAnimationEnabled(true);
    dialog.setText("DialogBox with DockLayoutPanel");
    dialog.setGlassEnabled(true);
    dialog.setAutoHideEnabled(true);
  }

  private void showit() {
    dialog.show();
    dialog.center();
  }

回避方法

いずれかの方法を使う。

  • 本当に必要になるまでDialogBoxの生成を遅らせる。一度正常に表示された後は、hide()/show()を繰り返しても正常。

  • SplitLayoutPanelをスプリッタサイズ0で使用する。ただし、この場合サイズはPXでしか指定できない。

  • 対象とするDockLayoutPanelDeckLayoutPanelに格納し、必要な時にshowWidgetする(検証中)。

Generator 'com.google.gwt.inject.rebind.GinjectorGenerator' threw an exception while rebinding ...

15:42:42.498 [ERROR] [.....] Generator 'com.google.gwt.inject.rebind.GinjectorGenerator' threw an exception while rebinding '.....'

java.lang.NullPointerException: null
    at com.google.gwt.inject.rebind.BindingsProcessor.createImplicitBinding(BindingsProcessor.java:498)
    at com.google.gwt.inject.rebind.BindingsProcessor.createImplicitBindingForUnresolved(BindingsProcessor.java:290)
    at com.google.gwt.inject.rebind.BindingsProcessor.createImplicitBindingsForUnresolved(BindingsProcessor.java:278)
    at com.google.gwt.inject.rebind.BindingsProcessor.process(BindingsProcessor.java:240)
    at com.google.gwt.inject.rebind.GinjectorGeneratorImpl.generate(GinjectorGeneratorImpl.java:76)
    at com.google.gwt.inject.rebind.GinjectorGenerator.generate(GinjectorGenerator.java:47)
    at com.google.gwt.core.ext.GeneratorExtWrapper.generate(GeneratorExtWrapper.java:48)

上のエラーは様々な理由で出るらしいが、エラーの理由が記述されていないため実際のエラーを補足するのは非常に難しい。上記のエラーが発生する一例としては、

Javaソースとしては正しいのにGWTとしては正しく無いソース

  public static final int SOMETHING = 12;

などと、うっかり「2」が全角になっている。これはJavaとしては正しいらしいが、GWTとしてはコンパイルできない。 この場合に上記例外が発生する。

Internal Server Error 500が発生する

開発環境では出現しないのに、本番環境に移行すると発生することがある。 プログラムとしてはどこも間違っていないし、コンパイルエラーも出ていない。

原因の一つとしては、クライアント・サーバ間でインターフェースや抽象クラスを受け渡そうとしていること。 例えば、HashMapの代わりにMapを使用すると発生する。

開発環境の他マシンでの表示

プログラム引数に

-bindAddress 0.0.0.0

を追加する。