今回はJava製Webアプリケーションフレームワークの1つであるWicketをご紹介します。
Wicketとは
Wicketはユーザインターフェイス(UI)を担当するフレームワークです。世の中にはJavaEEやPlay Framework、Ninjaのようにフルスタックなフレームワークも存在しますが、Wicketは、StrutsのようにUI層に特化しています。
事例としてはピザハットのサイトがあります。
Java製Webアプリケーションフレームワークは数多くありますが、Wicketの特徴はどこにあるのでしょうか。
Wicketの大事な2つの特徴
1.「Javaらしい」コードでWebアプリケーションを作れる
Wicket最大の特徴はオブジェクト指向を活用したJavaらしいコードでWebアプリケーションを作れることです。
オブジェクト指向の機能と言えば継承やカプセル化による部品化などでしょうか。Wicketはこれらの機能をうまく活用し、継承による画面レイアウトの共通化や、共通部品のクラス化などを可能にしています。
これによりJavaによるプログラミングを楽しみながらWebアプリケーションを作ることが出来るのです。
またほとんど全ての機能をJavaのコードとして記述することから、型安全性やリファクタリングしやすい、などの恩恵もあります。
2. Ajaxが統合されている
WicketはAjaxを上手に取り込んでいます。開発者は少しの作法を覚えるだけで、Ajaxを使った応答性の良いWebアプリケーションを作ることが出来るのです。
その他の特徴
- デフォルトでセキュア
- データと画面を柔軟に結合するModelの提供
- マルチタブやマルチウィンドウへの配慮
- 宣言的なマークアップ
- ユニットテスト用クラスの提供
この記事では大事な2つの特徴に絞ってご紹介しますが、更に詳しい情報を得たい方はWicketの本家Webサイトをご覧下さい。
第一章 最初のサンプル
1. Mavenによる雛形アプリの作成
まずはWebアプリ開発プロジェクトの雛形を作ります。
2015年7月にWicketは7系へのメジャーバージョンアップを果たしました。ここでは7系を使ってサンプルを作って行きましょう。
WicketはMavenのarchetypeを提供していますので、これを利用します。Mavenの導入は事前に済ませておいて下さい。
ターミナル、あるいはコマンドプロンプトで次のコマンドを実行して下さい。
mvn archetype:generate -DgroupId=sandbox -DartifactId=WicketSandbox -DarchetypeGroupId=org.apache.wicket -DarchetypeArtifactId=wicket-archetype-quickstart -DarchetypeVersion=7.0.0 -DarchetypeRepository=https://repository.apache.org/ -DinteractiveMode=false
groupIdとartifactIdは適切に変更して下さい。なおこのコマンドはWicketのQuickstartページで編集出来ます。
2. 雛形アプリの起動
mvnコマンドの実行が完了したら、動作する雛形アプリが出来上がっています。早速起動してみましょう。
cd WicketSandbox mvn jetty:run
WicketSandboxの部分はご自分のartifactIdに合わせて適切に変更して下さい。
コンソールにテキストがずらずらと表示され、下記のように「Started Jetty Server」と表示されれば起動が完了しています。
******************************************************************** *** WARNING: Wicket is running in DEVELOPMENT mode. *** *** ^^^^^^^^^^^ *** *** Do NOT deploy to your live server(s) without changing this. *** *** See Application#getConfigurationType() for more information. *** ******************************************************************** 2015-10-07 06:56:47.966:INFO:oejsh.ContextHandler:main: Started o.e.j.m.p.JettyWebAppContext@12d54304{/,file:/Users/jabaraster/Documents/Develop/Java/workspace/projects/WicketSandbox/src/main/webapp/,AVAILABLE}{file:/Users/jabaraster/Documents/Develop/Java/workspace/projects/WicketSandbox/src/main/webapp/} 2015-10-07 06:56:47.967:WARN:oejsh.RequestLogHandler:main: !RequestLog 2015-10-07 06:56:48.001:INFO:oejs.ServerConnector:main: Started ServerConnector@319f9f37{HTTP/1.1}{0.0.0.0:8080} 2015-10-07 06:56:48.303:INFO:oejs.ServerConnector:main: Started ServerConnector@4602f47{SSL-http/1.1}{0.0.0.0:8443} 2015-10-07 06:56:48.304:INFO:oejs.Server:main: Started @8721ms [INFO] Started Jetty Server
次のURLにアクセスして画面を確認してみましょう。
次の画面がブラウザに表示されれば起動に成功しています!
第二章 Wicketによる開発の概念を知る
雛形アプリの中身を見ていく前に、Wicketによる開発の考え方を押さえておきましょう。
1. HTMLとぺージクラスで画面を作る
WicketはHTMLで画面を作り、画面に対しページクラスを作ることでWebアプリケーションを構築します。
2. ページクラスの中にコンポーネントを配置
ページクラスの中には様々な種類のコンポーネントを配置します。コンポーネントはHTMLの一部に相当します。例えば<button></button>
タグに相当するButtonクラスなどがあります。Buttonクラスのような基本的なコンポーネントはもちろん、多くのコンポーネントがWicketから提供されています。また必要であれば自作することも出来ます。コンポーネントを知ることはWicketを使う上で重要なポイントになります。
3. コンポーネントは入れ子に出来る
コンポーネントによっては、中に1個以上の子コンポーネントを持つことが出来ます。コンポーネントは入れ子に出来るわけです。
実はページクラスもコンポーネントです。Wicketのページクラスは、ページクラスを根本としたコンポーネントのツリー構造となります。
第三章 HTMLとぺージクラスのポイントを押さえる
Wicketで最も重要なのはHTMLとぺージクラスの作り方です。ここでは雛形アプリを使ってポイントを押さえておきます。
雛形アプリのファイル構成を見ると、「HomePage」から始まるファイルが2つあります。ぺージクラスと、それに対応するHTMLファイルです。
まずはぺージクラスのコードを見てみましょう。
package sandbox; import org.apache.wicket.request.mapper.parameter.PageParameters; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.WebPage; public class HomePage extends WebPage { private static final long serialVersionUID = 1L; public HomePage(final PageParameters parameters) { super(parameters); // Labelコンポーネントをぺージクラスに追加z add(new Label("version", getApplication().getFrameworkSettings().getVersion())); // TODO Add your page's components here } }
ポイントを挙げておきます。
- 7行目:ぺージクラスはWebPageクラスを継承
- 14行目:ぺージクラスの子としてLabelコンポーネントを追加
次にHTMLを見てみます・・・と言いたいところですが、雛形アプリのHTMLファイルはかなりコード量が多く大事な部分が見えにくいですから、思い切って重要でない部分を落としてしまいましょう。次のようにHTMLを書き換えて下さい。
<!DOCTYPE html> <html xmlns:wicket="http://wicket.apache.org"> <head> <meta charset="utf-8" /> <title>Apache Wicket Quickstart</title> </head> <body> Wicketのバージョン: <span wicket:id="version">ここに使っているWicketのバージョンが入ります</span> </body> </html>
何の変哲もないHTMLですが、1つだけ変な記述がありますね。8行目のwicket:id属性です。
wicket:id属性は、HTMLのタグをページクラスで追加したコンポーネントと関連付けるものです。ぺージクラスのコードのコンポーネント追加部分を再掲します。
add(new Label("version", getApplication().getFrameworkSettings().getVersion()));
Labelクラスのコンストラクタの第一引数に注目して下さい。”version”となっており、HTMLのwicket:id属性の値”version”と一致しています。ここがポイントです。
Wicketでは、コンポーネントの第1引数に必ずIDを取るようになっています。このIDとHTML上のwicket:id属性の値を一致させることで、コンポーネントと関連付けられます。
以上がWicketを使う上で最低限知っておかなければならないことです。
第四章 コンポーネントを使う
実際にWebアプリケーションを作るには、種々のコンポーネントの知識が必要です。Wicketは様々なコンポーネントを提供していますので、うまく使えばあまり深く考えなくてもリッチなUIを実現出来ます。ここからは代表的なコンポーネントの使い方を見てみましょう。
1. 予備知識・匿名クラスについて
コンポーネントを使う上でよく使うJavaの機能に匿名クラスというものがあります。匿名クラスは簡単に言えばnewしたオブジェクトの一部メソッドをオーバーライドする機能です。
次のように記載します。
final Map<String, List<String>> map = new HashMap<String, List<String>>() { @Override public List<String> get(final Object pKey) { List<String> ret = super.get(pKey); if (ret == null) { ret = new ArrayList<>(); put((String) pKey, ret); } return ret; } };
コンストラクタ呼び出しの直後に{}を記載し、中でメソッドをオーバーライドします。上記の例ではHashMapクラスをnewすると同時にgetメソッドをオーバーライドしています。この構文はWicketを使っていると頻出しますので慣れておいて下さい。
2. Ajaxコンポーネントを使う
まずはAjaxコンポーネントの使い方を見てみましょう。いきなりAjax?と思われるかもしれませんがWicketはAjaxをとても簡単に扱うことが出来るのです。ここでは、ボタンを押す度にサーバの時刻をAjaxで取得して表示する機能を追加してみましょう。
まずサーバの時刻を表示するコンポーネントを追加します。
final Label now = new Label("now", new AbstractReadOnlyModel<String>() { @Override public String getObject() { return DateFormat.getDateTimeInstance().format(new Date()); } }); now.setOutputMarkupId(true); add(now);
7行目のnow.setOutputMarkupId(true)
を忘れないようにして下さい。これはAjaxで書き換えるコンポーネントに対して必要な記述です。
次にリンクコンポーネントを追加します。
final AjaxLink<?> nowRefresher = new AjaxLink<Object>("nowRefresher") { @Override public void onClick(final AjaxRequestTarget pTarget) { pTarget.add(now); } }; add(nowRefresher);
このコードの意味は、「リンクを押したらnowコンポーネントを再表示しなさい」です。
最後にHTMLを編集します。Javaコードで追加した2つのコンポーネントをHTMLにも追記しましょう。
<h2 wicket:id="now">サーバの時刻がここに表示されます</h2> <a wicket:id="nowRefresher">サーバ時刻表示を更新</a>
次のURLにアクセスして画面を確認してみましょう。
「サーバ時刻表示を更新」というリンクをクリックする度に時刻表示が更新されるのが分かると思います。
このように、WicketはAjaxを「コンポーネントを再表示する」機能として取り込むことで、プログラマが楽にAjaxを利用出来るようにしているのです。
第五章 コレクションを扱う
次に紹介するコンポーネントはコレクションを扱うものです。一覧表示はWebアプリケーションで欠かせないものですから、コレクションの扱い方を押さえておくのは不可欠です。
コレクションを扱うコンポーネントには様々な種類があるのですが、ここでは最も一般的なListViewを使います。以下、ListViewの生成とページクラスへの追加を行うコードです。
final List<String> nameValues = Arrays.<String> asList("JavaEE6", "Ninja", "Play Framework", "Wicket"); final ListView<String> names = new ListView<String>("names", nameValues) { @Override protected void populateItem(final ListItem<String> pItem) { pItem.add(new Label("name", pItem.getModelObject())); } }; add(names);
ここでも匿名クラスが登場しています。オーバーライドしているpopulateItemメソッドは、コレクションの要素毎に呼び出されます。引数のListItemにコンポーネントを追加することで画面を編集出来ます。ここではLabelのみを追加し、シンプルに文字列を表示しています。
併せてHTMLも編集しましょう。
<h2>Javaフレームワーク</h2> <ul> <li wicket:id="names"><span wicket:id="name">ここにコレクションの要素が表示されます</span></li> </ul>
次のURLにアクセスして画面を確認してみましょう。
いかがでしょうか。多少クセがありますが、Wicketでコレクションを扱うときの基本形は以上のようになります。
その他のコンポーネント
他にも数多くの便利なコンポーネントがあるのですが、とてもではありませんが紹介するスペースが足りませんので、重要なものの名前を挙げるに留めます。
- Form/TextFieldなど:ユーザからの入力を受け取るフォーム部品が提供されています
- AjaxButton/IndicatingAjaxButton:Ajaxでサブミットするボタンです
- AjaxFallbackDefaultDataTable:Ajaxによるページング機能を持つテーブルです
第六章 コンポーネントを作る
最後に、簡単なコンポーネントを作ってみましょう。Ajaxコンポーネントの紹介で作ったサーバ時刻を表示する機能をコンポーネント化してみます。
HTMLを持つコンポーネントを作るには、Panelクラスを継承します。ここではServerTimePanelというクラスを作ってみましょう。
package sandbox; import java.text.DateFormat; import java.util.Date; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.markup.html.AjaxLink; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.model.AbstractReadOnlyModel; public class ServerTimePanel extends Panel { public ServerTimePanel(final String pId) { super(pId); final Label now = new Label("now", new AbstractReadOnlyModel<String>() { @Override public String getObject() { return DateFormat.getDateTimeInstance().format(new Date()); } }); now.setOutputMarkupId(true); add(now); final AjaxLink<?> nowRefresher = new AjaxLink<Object>("nowRefresher") { @Override public void onClick(final AjaxRequestTarget pTarget) { pTarget.add(now); } }; add(nowRefresher); } }
HomePage.javaからコンポーネント生成と追加部分を移植しました。
次にHTMLファイルを作成します。クラスと同名、つまりServerTimePanel.htmlを作りましょう。
<wicket:panel> <h2 wicket:id="now">サーバの時刻がここに表示されます</h2> <button type="button" wicket:id="nowRefresher">サーバ時刻表示を更新</button> </wicket:panel>
<wicket:panel>〜</wicket:panel>
で囲んだ部分がコンポーネントとして使われるHTMLになります。
これでコンポーネントは完成。使う側のページクラスを修正しましょう。
HomePage.javaは次のようになります。
package sandbox; import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.request.mapper.parameter.PageParameters; public class HomePage extends WebPage { public HomePage(final PageParameters parameters) { super(parameters); add(new Label("version", getApplication().getFrameworkSettings().getVersion())); add(new ServerTimePanel("serverTime")); } }
12行目でServerTimePanelを使っています。ご覧の通り、自作のコンポーネントだからといって特別なことは何もなく、Labelなどのコンポーネントと同じように扱えます。
HomePage.htmlは次のようになります。
<!DOCTYPE html> <html xmlns:wicket="http://wicket.apache.org"> <head> <meta charset="utf-8" /> <title>Apache Wicket Quickstart</title> </head> <body> Wicketのバージョン: <span wicket:id="version">ここに使っているWicketのバージョンが入ります</span> <hr/> <div wicket:id="serverTime"></div> </body> </html>
10行目でServerTimePanelコンポーネントを使っています。こちらも特別なことは何もないですね。実際にブラウザ上でどう表示されるかは、皆さんの目で確かめて下さい。
まとめ
かなり駆け足でWicketの概要と基本を見てきました。Ajaxの利用や部品化が簡単に行えることが理解していただけたかと思います。
Javaの利点を最大限に発揮することを目指して作られているWicketは、機能や設定項目にうまくJavaの型を当てることで、誤解やミスが生じにくいAPIを提供しています。
簡単かつ安全にWebアプリケーションを作れるWicketにぜひ触れてみて、一味違うフレームワークを味わってみて下さい。