GWTでlombokを使用する
- eclipseにlombokをインストール
- (lombokを使用してjavaファイルを作成)
- GWT Compile Project -> Advanced -> VM arguments: に次の文を追加
- javaagent:lib/lombok.jar=ECJ (プロジェクトのlibフォルダにlombok.jarを入れているとき)
「(ゲッター名)is undefined for the type」のようなエラーが出たとき
対象のフィールドにいったん @Getterをつけてから再コンパイルすると直ることがある。
参考:
Project Lombok
@Data @Model(schemaVersion = 1) public class MyModel implements Serializable { private static final long serialVersionUID = 1L; @Attribute(primaryKey = true) private Key key; @Attribute(version = true) private Long version; // @Getter private String title; @Attribute(unindexed=true) private String content; @Attribute(lob=true) private String description; }
slim3プロジェクトで新しいバージョンのGAEを使用する
記事作成日:2015.4.9
slim3プラグインはアップデートされていないようで、そのままプロジェクトを作成すると GoogleAppEngine SDKのバージョンが1.6.0になる。
slim3関連ファイルを新しいSDKに移動しても動作するみたいなので手順をまとめる。
空のslim3プロジェクトを作成する
本命のプロジェクトの作成
- New Project -> Google -> New Web Application Project
- 以下のファイルを移動
プロジェクトルートディレクトリの
build.properties
build.xml
war/WEB-INF/lib ディレクトリの
junit-4.7.jar
ktrwjr.jar
slim3-1.0.15.jar
- 以下のフォルダを移動
プロジェクトルートディレクトリの
lib
libsrc
slim3側のweb.xmlの中身を本命のweb.xmlへ移す。
このときエラーが出るので<web-resource-name>を追加する
<web-resource-collection> <url-pattern>*.jsp</url-pattern> </web-resource-collection>
↓
<web-resource-collection> <web-resource-name></web-resource-name> <url-pattern>*.jsp</url-pattern> </web-resource-collection>
パッケージ名も変更
<context-param> <param-name>slim3.rootPackage</param-name> <param-value>(空のslim3プロジェクトのパッケージ名)</param-value> </context-param>
↓
<context-param> <param-name>slim3.rootPackage</param-name> <param-value>(本命のプロジェクトのパッケージ名)</param-value> </context-param>
- ビルドパスの編集
プロジェクト内にある
lib\slim3-gen-1.0.15.jar
war\WEB-INF\lib\junit-4.7.jar
war\WEB-INF\lib\slim3-1.0.15.jar
への参照を追加
<inherits name='org.slim3.gwt.emul.S3Emulation' />
- アノテーションプロセッサの設定
(メニューバー) -> Project -> Properties -> Java Compiler -> Annotation Processing
アノテーションプロセッシングを有効にする
-> Factory Path
以下のjarファイルを追加
-
- lib/slim3-gen-x.x.xx.jar
GWTのRPCで、特定のフィールドは送受信したくないとき
GWTのRPCで、特定のフィールドは送受信したくないときは、クラスのフィールドにtransient修飾子をつけるとよい。
アノテーションをつけて抑制する方法とかもありそうだけどわからなかった。
なお、クライアント - サーブレット間で送受信したいが、データベースには保存したくないフィールドは
@Attribute(persistent=false)
のようにアノテーションをつける
使用例
//データモデルにコンポジットパターンとリスナーパターンを使用するとき。 @Model(schemaVersion = 1) public abstract class Composite implements Serializable{ @Attribute(primaryKey = true) private Key key; @Attribute(version = true) private Long version; //データベースにComposite役の参照関係を保存するとき //Composite役をデータベースに直接保存するとデータ容量がすごいことになる。 //Composite役への参照はサーブレット - クライアント間で送受信するが、 //データベースには保存しない。 @Attribute(persistent=false) protected List<Composite> childrenList = new ArrayList<Composite>(); //子要素のリスト //データベース内で子要素との関連を保持するためのリスト //本当はInverseModelListRefを使った方がいいかも。 @Attribute(unindexed=true) private List<Key> childrenKeyList = new ArrayList<Key>(); //オブザーバパターンのためのリスナー // RPCでサーバに送ろうとすると、リスナー側のパネルまでサーバに送ってしまい、NotSerializableExceptionがでる。 @Attribute(persistent=false) transient protected List<ItemListener> _listeners = new ArrayList<ItemListener>(); //(後略) }
データベース - サーブレット - クライアント間のデータ送受信の抑制
○:送受信する! ×:抑制
クラスのフィールドにtransient修飾子をつける | フィールドに @Attribute(persistent=false) アノテーションをつける | ? | |
---|---|---|---|
DB | |||
× | × | ○ | |
Servlet | |||
× | ○ | × | |
Client |
GWTでサーバを使用せずにZipファイルをクライアントへ保存
外部JavaScript(JS)ライブラリとJSNIを使って実装する。FileSaverとJSZipを利用する。
Shift_JISは扱えません!!
- 使用するjsライブラリ
- jszip.js JSZip
- FileSaver.js eligrey/FileSaver.js · GitHub
- war/js に使用するJSファイルを入れておく
- htmlファイルでJSファイルをリンクしておくのも忘れずに
- javaファイルでは、JSNIで外部JSライブラリにアクセス($wndを忘れずに)。JSNI内部ではjava.util.Mapが使えないようなので、JSONObjectを使用する。
- JSONObjectを使うために、.gwt.xmlファイルにinheritsを追加する必要がある。
参考
gwt - Java Hashmap and Mutlidimensional array type sig in JSNI? - Stack Overflow
eligrey/FileSaver.js · GitHub
JSZip
*.html
(前略) <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <link type="text/css" rel="stylesheet" href="/css/global.css" /> <script language="javascript" src="js/jszip.js"></script> <script language="javascript" src="js/FileSaver.js"></script> </head> (後略)
*.java
public void onModuleLoad() { Button save = new Button("保存"); save.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { //JSONObject がMap代わりに使える JSONObject obj = new JSONObject(); for (int i = 0; i < 3; i++) { obj.put("k"+i, new JSONString("v"+i)); } JavaScriptObject jsObj = obj.getJavaScriptObject(); saveAsZip(jsObj); } }); RootPanel.get().add(save); } /** * @param fileMap * key:ファイル名 value:ファイル内容 * @return */ private native boolean saveAsZip(JavaScriptObject fileMap) /*-{ var zip = new $wnd.JSZip(); for (key in fileMap) { zip.file(key + ".txt", fileMap[key]); } var blob = zip.generate({ type : "blob" }); $wnd.saveAs(blob, "text.zip"); return true; }-*/;
<module> <inherits name="com.google.gwt.user.User" /> <inherits name="com.google.gwt.user.theme.standard.Standard" /> <inherits name="com.google.gwt.json.JSON" /> <entry-point class="(エントリーポイントクラス)" /> <source path="client" /> </module>
FlowPanelの要素にPanelを追加しても浮動化してくれない
GWTを使用していて、FlowPanelの要素にPanelを追加しても浮動化してくれないとき。
追加する要素を<span></span>で囲み、CSSでfloat属性を指定すればよい
参考:
Simplifying GWT Markup with HTML Widgets « TurboManage
Main.java
public class Gwt_tmp implements EntryPoint { private static final int COUNT = 10; public void onModuleLoad() { VerticalPanel mainPanel = new VerticalPanel(); RootPanel.get().add(mainPanel); mainPanel.addStyleName("contents"); { mainPanel.add(new Label("ボタンをFlowPanelに追加")); FlowPanel fp = new FlowPanel(); fp.setWidth("800px"); mainPanel.add(fp); fp.clear(); for (int i=0; i<COUNT; i++) { Button item= new Button("button"+i); item.setWidth("100px"); fp.add(item); } } mainPanel.add(new HTML("<hr>"));[f:id:black-skin:20150315175347p:plain] { mainPanel.add(new Label("VerticalPanelはFlowPanelでも横配置できない")); FlowPanel fp = new FlowPanel(); mainPanel.add(fp); for (int i = 0; i < COUNT; i++) { VerticalPanel item = new VerticalPanel(); item.setStyleName("name-" + i); item.add(new Label("title" + i)); item.add(new Button("ボタン")); fp.add(item); } } mainPanel.add(new HTML("<hr>")); { mainPanel.add(new Label("<span>で囲み、floatを指定")); FlowPanel fp = new FlowPanel(); fp.setWidth("800px"); mainPanel.add(fp); fp.clear(); for (int i=0; i<COUNT; i++) { VerticalPanel item = new VerticalPanel(); item.setWidth("150px"); item.setStyleName("name-" + i); item.add(new Label("title" + i)); item.add(new Button("ボタン")); fp.add(new FloatSpanItemWidget(item)); } } } }
FloatSpanItemWidget.java
import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.Widget; public class FloatSpanItemWidget extends SimplePanel { public FloatSpanItemWidget() { super((Element) Document.get().createSpanElement().cast()); addStyleName("float"); } public FloatSpanItemWidget(String s) { this(); getElement().setInnerText(s); } public FloatSpanItemWidget(Widget w) { this(); this.add(w); } }
<!--(中略)--> .float{ float: left; }