とりあえず雑記帳(跡地)
Slim3でGWT
最終更新:
fujiyan
-
view
WebコミックLibraryhttp://web-comi.appspot.com/ GAE/JとSlim3で作成してみた、各出版社から配信されているWebコミックをまとめて閲覧できるサイトです。只今、実験運用中… |
Eclipse環境設定
Slim3に記載している内容と同じ
プロジェクト作成
- 基本的に、公式サイトのGetting Started with GWTの通りに行う。だいたい以下の通り。
- ダウンロードしたzipを解凍する。
- 解凍して作成されたフォルダ"slim3-blank"を、Eclipseのワークスペースにインポートする。
- インポートしたプロジェクトの名前を、Refactorで変更する。
- プロジェクトのコンテキストメニュー→[Properties]→[Resource]の[Text file encoding]をUTF-8にして、デフォルトのファイルエンコーディングをUTF-8にする。これをしておかないと、日本語表示が面倒なことになる。
- これによって、JavaソースファイルもUTF-8になるので注意。
- プロジェクトのプロパティダイアログを開き(プロジェクトのコンテキストメニュー→[Properties])、[Google]→[Web Toolkit]の[Use Google Web Toolkit]をチェックし、プロジェクトでGWTを利用するように設定する。
- war/WEB-INF/web.xmlのcontext-paramのslim3.rootPackage(で指定したパッケージ)に、作成するソースのルートパッケージ名を設定する。
<context-param> <param-name>slim3.rootPackage</param-name> <param-value>jp.fujiyan.gae.example.slim3.gwt</param-value> </context-param>
- war/WEB-INF/web.xmlのGWTServiceServletのサーブレット定義(<servlet>要素)とサーブレットマッピング(<servlet-mapping>要素)のコメントアウトを解除し、有効にする。
<servlet> <servlet-name>GWTServiceServlet</servlet-name> <servlet-class>org.slim3.gwt.server.rpc.GWTServiceServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet>
<servlet-mapping> <servlet-name>GWTServiceServlet</servlet-name> <url-pattern>*.s3gwt</url-pattern> </servlet-mapping>
- プロジェクトのプロパティダイアログを開き(プロジェクトのコンテキストメニュー→[Properties])、[Java Compiler]→[Annotation Processing]で、[Enable project specific settings]のチェックをONにし、[Enable annotation processing]のチェックもONにし、[Enable processing editor]のチェックはOFFにする。また、[Generated source directory]テキストボックスの内容を"src"とする。
- プロジェクトのプロパティダイアログを開き(プロジェクトのコンテキストメニュー→[Properties])、[Java Compiler]→[Annotation Processing]→[Factory Path]の[Add JARs]で、libフォルダの下にあるslim3-gen-xxx.jar(slim3-gen-1.0.2.jar)を追加する。
GWT Service作成
- gen-gwt-serviceターゲットでServiceを自動作成する。
- build.xmlを開く(ダブルクリック等)。
- Outlineビューからgen-gwt-serviceターゲットを選択し右クリック→[Run As]→[Ant Build]を選択する
- [Ant Input Request]ダイアログの[Input a service name.]に、サブパッケージ名+Service名を入力すると、パッケージとServiceが作成される。
- 以下のファイルが作成される。
- [slim3.rootPackage].client.service.[サブパッケージ名].[サービス名]Service
- [slim3.rootPackage].client.service.[サブパッケージ名].[サービス名]ServiceAsync
- [slim3.rootPackage].server.service.[サブパッケージ名].[サービス名]ServiceImpl
これだと、Module分割した際に、[slim3.rootPackage]のサブパッケージにModuleを作った場合どうしよう?
→Moduleは基本的に[slim3.rootPackage]直下に置け、とのこと。だからサブパッケージにModuleを作る場合がナシ
Serviceにメソッドを定義する
- メソッドint foo(String bar)を定義する場合、以下の3つを実施
- [サービス名]Serviceに、メソッドのシグネチャを定義する。
public interface XxxService extends RemoteService { int foo(String bar); }
- [サービス名]ServiceAsyncに、メソッドのシグネチャを定義する。但し、メソッドに引数にAsyncCallback<戻り値の型>を追加する。
public interface XxxServiceAsync { void foo(String bar, AsyncCallback<int> callback); }
- [サービス名]ServiceImplに、メソッドの実装を定義する。
public class XxxServiceImpl implements XxxService { public String foo(String bar) { なんか実装 } }
- サービスの引数や戻り値に用いる型は、GWTコンパイラーでJavaScriptに変換できるもの(基本データ型、java.langやjava.utilパッケージのクラス、Moduleの<source>で指定したパッケージのクラス(Serializableをimplement))しか無理っぽい。
- 例えば、どっかのサードパーティが作ったjarファイルにあるクラスを利用しているクラスは使えないっぽい("did you forget to inherit a required module?"と言われる)
- 筆者がハマったのは、自作の(jarファイル化した)ライブラリをextendsしたModelを戻り値にしたら駄目だった、Modelの内部でICU4Jを利用したら駄目だった等
- じゃあModelからそういうクラスを用いなくすれば良い、というのも解決策の1つだけど、GWTの都合でModelの作りを変えるのもヤだ。
- なので、Modelのプロパティのみを定義したDTOクラスを<source>パッケージに定義し、Serviceの引数/戻り値はそのDTOクラスにし、Serviceの実装メソッド内でDTO←→Modelの変換を行う
- でもまぁ、ModelはGWTで利用できる程度に、極力簡単にしておくのも一案。
- 自作ライブラリはModule jarファイル(後述)にしてしまう。
- 自作でないライブラリを使う場合は、出来るだけサーバ側のServiceImpl内で利用する。
Serviceの呼び出し
- Googleのサンプルを見ると、XxxServiceにSingletonとして定義し、アプリケーションからはそれを用いるようにしている。
public interface XxxService extends RemoteService { public static class App { private static final XxxServiceAsync instance = (XxxServiceAsync) GWT.create(XxxService.class); public static XxxServiceAsync getInstance() { return instance; } } ... }
呼び出し側
XxxServiceAsync service = XxxService.App.getInstance(); // クラスメンバにしておいても良い service.foo("引数", new AsyncCallback<String>() { public void onSuccess(String results) { // 成功時処理 } public void onFailure(Throwable caught) { // 失敗時処理 } });
- Serviceの戻り値は、onSuccess()の引数に渡される。
Service内でSessionにアクセス
- org.slim3.util.RequestLocatorのget()メソッドでHttpServletRequestを取得する。後は通常通りgetSession()で取得する。
GWT UiBinderとSlim3 Controllerでファイルアップロード
- GWTのUiBinderでクライアントを作成し、Slim3のControllerでファイルを受け取り、結果(HTML)をクライアントに表示する。
- ファイルのアップロード先にGWT Serviceは指定できない。そもそもformのsubmitじゃないとアップロードは無理。
- GWTのFileUploadはFormPanel配下に配置する必要がある。
- FormPanelには、encoding="multipart/form-data"とmethod="post"を指定しておく。
- 結果の表示については、クライアントにHTML(widget)を置き、FormPanelのonSubmitCompleteイベントハンドラ内で、ControllerからのレスポンスのHTMLをHTML(widget)に設定する。
- 以下は、アップロードしたファイル(テキスト限定)をそのまま結果表示するサンプル。
UploadForm.ui.xml
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent"> <ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder" xmlns:g="urn:import:com.google.gwt.user.client.ui"> <ui:style> </ui:style> <g:HTMLPanel> <g:FormPanel ui:field="uploadFormPanel" action="upload" method="post" encoding="multipart/form-data"> <g:VerticalPanel> <g:FileUpload name="data"></g:FileUpload> <g:SubmitButton>アップロード</g:SubmitButton> </g:VerticalPanel> </g:FormPanel> <g:HTML ui:field="resultHtml"/> </g:HTMLPanel> </ui:UiBinder>
UploadForm.java
public class UploadForm extends Composite { private static UploadFormUiBinder uiBinder = GWT.create(UploadFormUiBinder.class); interface UploadFormUiBinder extends UiBinder<Widget, UploadForm> { } @UiField FormPanel uploadFormPanel; @UiField HTML resultHtml; public UploadForm(Map<String, String> parameterMap) { initWidget(uiBinder.createAndBindUi(this)); } @UiHandler("uploadFormPanel") public void onSubmitComplete_uploadFormPanel(SubmitCompleteEvent e) { resultHtml.setHTML(e.getResults()); }
UploadController.java
public class UploadController extends Controller { @Override public Navigation run() throws Exception { FileItem fileItem = requestScope("data"); requestScope("test", new String(fileItem.getData())); return forward("upload.jsp"); } }
upload.jsp
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>admin Upload</title> </head> <body> <p><c:out value="${test}"/></p> </body> </html>