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, "");Base64Coderはhttp://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を削除すればよい。
apache/tomcatの連携
通常、gwtアプリをwarファイルとしてtomcatに配備し、apacheと連携させ、ユーザ側にはapache側にアクセスさせることと思われる。 その際、例えばgwtアプリのコンテキストパスがfoobarであるのに、apache側ではルートとしてアクセスさせるような以下のような設定を行うと、
<Location /> ProxyPass ajp://localhost:8009/foobar/ </Location>
以下のようなエラーが発生する。
Apr 5, 2012 2:37:43 PM org.apache.catalina.core.ApplicationContext log
INFO: Key[type=com.gwtplatform.dispatch.server.guice.DispatchServiceImpl, annotation=[none]]: ERROR: The module path requested, /foobar/, is not in the same web application as this servlet, /foobar. Your module may not be properly configured or your client and server code maybe out of date.
Apr 5, 2012 2:37:43 PM org.apache.catalina.core.ApplicationContext log
INFO: Key[type=com.gwtplatform.dispatch.server.guice.DispatchServiceImpl, annotation=[none]]: WARNING: Failed to get the SerializationPolicy 'BFA4FE22E9D21F283BBB8900278EB97B' for module 'http://****/foobar/'; a legacy, 1.3.3 compatible, serialization policy will be used. You may experience SerializationExceptions as a result.
Apr 5, 2012 2:37:43 PM org.apache.catalina.core.ApplicationContext log
SEVERE: Key[type=com.gwtplatform.dispatch.server.guice.DispatchServiceImpl, annotation=[none]]: An IncompatibleRemoteServiceException was thrown while processing this call.
com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException: Type '***' was not assignable to 'com.google.gwt.user.client.rpc.IsSerializable' and did not have a custom field serializer. For security purposes, this type will not be deserialized.
at com.google.gwt.user.server.rpc.RPC.decodeRequest(RPC.java:315)
at com.google.gwt.user.server.rpc.RemoteServiceServlet.processCall(RemoteServiceServlet.java:206)
at com.google.gwt.user.server.rpc.RemoteServiceServlet.processPost(RemoteServiceServlet.java:248)apache側でルートコンテキストとしてアクセスさせたい場合は、tomcat側でもルートコンテキストとして配備しなければならないらしい。
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でしか指定できない。
対象とするDockLayoutPanelをDeckLayoutPanelに格納し、必要な時にshowWidgetする(検証中)。
