【レポート】

Wicket入門 - JavaとHTMLだけで作るWebアプリケーション

7 画像の生成

    杉山貴章  [2006/05/09]

    最後に、応用としてフォームから入力されたテキストを元に、Wicketで提供されるライブラリを利用して画像を生成する例を紹介する。また、この例はPOJOの持つプロパティをコンポーネントに表示する方法も含んでいる。これには先の例でも触れたWicketのモデルオブジェクトを使用する。

    具体的には、1ページ目で図のように表示し、Submitボタンが押されたらテキストエリアに入力された文字列が埋め込まれた画像を生成して2ページ目に表示するというアプリケーションにする。したがってExamplePage.htmlのbody部分はリスト7.2のようになる。

    リスト7.2 ExamplePage.html

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=Shift-JIS">
    <title>Example</title>
    </head>
    <body>

            <p wicket:id="message"></p>
            <form wicket:id="form" acceptcharset="Shift-JIS">
                    <textarea wicket:id="textarea" cols="30" rows="10"></textarea>
                    <input wicket:id="submit" type="submit" value="Submit"/>
                    <input wicket:id="clear" type="submit" value="Clear"/>
            </form>


    </body>
    </html>

    次に、リスト7.3のようにExamplePageBeanクラスを用意する。これはExamplePageに表示する内容を格納するJavaBeansで、"message"と"textarea"という2つのプロパティを持つ。このプロパティはそれぞれ表示するテキストとテキストエリアを表す。

    リスト7.3 ExamplePageBean.java

    package jp.co.ongs.example;

    import java.io.Serializable;

    public class ExamplePageBean implements Serializable {
            private static final long serialVersionUID = 1L;
            
            private String message;
            private String textarea;

            public String getMessage() {
                    return message;
            }
            public void setMessage(String message) {
                    this.message = message;
            }
            public String getTextarea() {
                    return textarea;
            }
            public void setTextarea(String textarea) {
                    this.textarea = textarea;
            }
    }

    そしてExamplePage.javaはリスト7.4のようになる。

    リスト7.4 ExamplePage.java

    package jp.co.ongs.example;

    import wicket.markup.html.WebPage;
    import wicket.markup.html.basic.Label;
    import wicket.markup.html.form.Button;
    import wicket.markup.html.form.TextArea;
    import wicket.markup.html.form.Form;
    import wicket.markup.html.form.validation.RequiredValidator;
    import wicket.markup.html.panel.FeedbackPanel;
    import wicket.model.BoundCompoundPropertyModel;

    public class ExamplePage extends WebPage {
            private static final long serialVersionUID = 1L;

            static final String TEXTAREA_ID = "textarea";

            private ExamplePageBean bean;

            public ExamplePage() {
                    //Beanオブジェクトを生成してプロパティをセット
                    this.bean = new ExamplePageBean();
                    this.bean.setMessage("画像に変換する文字列を入力してください.");
                    this.bean.setTextarea("");

                    // Beanのプロパティをコンポーネントに割り当てるためにPropertyModelを生成
                    BoundCompoundPropertyModel model = new BoundCompoundPropertyModel(bean);
                    super.setModel(model);

                    // ラベルを作成してPropertyModelにバインド
                    super.add(model.bind(new Label("message"), "message"));

                    // フォームを作成してPropertyModelにバインド
                    Form form = new Form("form");
                    TextArea textarea = new TextArea("textarea");
                    textarea.add(RequiredValidator.getInstance());
                    form.add(model.bind(textarea, "textarea"));
                    Button submitButton = new Button("submit") {
                            private static final long serialVersionUID = 1L;
                            protected void onSubmit() {
                                    submit();
                            }
                    };
                    Button clearButton = new Button("clear") {
                            private static final long serialVersionUID = 1L;
                            protected void onSubmit() {
                                    clear();
                            }
                    };
                    form.add(submitButton);
                    form.add(clearButton);

                    // フォームとパネルをページに追加
                    super.add(form);
                    super.add(new FeedbackPanel("feedback"));
            }
            
            /**
             * リクエストの開始時に実行されるアクション
             */
            protected void onBeginRequest()
            {
                    super.onBeginRequest();

                    // セッションを取得してテキストエリアを初期化
                    ExampleSession session = (ExampleSession)super.getSession();
                    String text = (String)session.get(TEXTAREA_ID);
                    this.bean.setTextarea(null != text ? text : "");
            }
            
            /**
             * clearボタンの押下に割り当てられたアクション
             */
            private void clear()
            {
                    // セッションを取得してテキストエリアをクリア
                    ExampleSession session = (ExampleSession)super.getSession();
                    session.put(TEXTAREA_ID, (Object)"");
                    super.setResponsePage(ExamplePage.class);
            }

            /**
             * Submitボタンの押下に割り当てられたアクション
             */
            private void submit() {
                    // セッションを設定して次のページに移動
                    ExampleSession session = (ExampleSession)super.getSession();
                    session.put(TEXTAREA_ID, (Object)this.bean.getTextarea());
                    super.setResponsePage(NextPage.class);
            }
    }

    少し長いので順番に解説すると、まずExamplePageクラスにはprivateフィールドとしてExamplePageBeanを宣言してあり、コンストラクタのリスト7.5の部分でオブジェクトを生成してプロパティをセットしている。

    リスト7.5 Beanオブジェクトの作成

                    // Beanオブジェクトを生成してプロパティをセット
                    this.bean = new ExamplePageBean();
                    this.bean.setMessage("画像に変換する文字列を入力してください.");
                    this.bean.setTextarea("");

    続いて、Beanオブジェクトのプロパティをコンポーネントと関連付けるためのモデルとしてwicket.model.BoundCompoundPropertyModelオブジェクトを生成し、これをページにセットする(リスト7.6)。

    リスト7.6 Modelの作成

                    // Beanのプロパティをコンポーネントに割り当てるためにPropertyModelを生成
                    BoundCompoundPropertyModel model = new BoundCompoundPropertyModel(bean);
                    super.setModel(model);

    FormやButtonなどのコンポーネントの生成についてはこれまでの例と同様である。ただしLabelとTextAreaに限っては、コンポーネントに表示する内容をBeanのプロパティから取得したい。そこで、それぞれリスト7.7のようにしてBoundCompoundPropertyModelオブジェクトにバインドしている。

    リスト7.7 コンポーネントをモデルにバインド

                    super.add(model.bind(new Label("message"), "message"));
                    form.add(model.bind(textarea, "textarea"));

    なお、今回はTextAreaオブジェクトがnullだったりテキストが空のままだったりすると画像が生成できないため、それを検証するためにwicket.markup.html.form.validation.RequiredValidatorオブジェクトをTextAreaにセットしている。この検証のためにプロパティファイルとしてリスト7.8のような内容のExamplePage.propertiesを用意する。

    リスト7.8 ExamplePage.properties

    form.textarea.RequiredValidator=text is required

    ExamplePage.javaのその他の部分はこれまでの例でやっている内容と同様である。なお、onBeginRequest()メソッドはリクエストの開始時に自動的に実行されるメソッドで、今回はテキストエリアを初期化するためにオーバーライドしている。セッションを維持するためのExampleSessionクラスは前回の例と同じものを使用する。

    続いて画像を生成して表示するNextPageを作成する。まず、HTML側はリスト7.9のようになる。画像を表示するのでimgタグを使用し、それに"image"というwicket:idを付加している。また、[Back]ボタンを付けるためにExamplePage同様フォームを使用している。

    リスト7.9 NextPage.html

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
    <title>NextPage</title>
    </head>
    <body>

            <p wicket:id="message"></p>
            <img wicket:id="image" border="1"/><br/>
            <form wicket:id="form">
                    <input wicket:id="back" type="submit" value="Back"/>
            </form>

    </body>
    </html>

    これに対応するNextPage.javaはリスト7.10のようになる。Wicketには動的に画像を生成して描画するためにwicket.markup.html.image.resource.RenderedDynamicImageResourceというクラスが用意されている。このクラスのrender()メソッドを独自に実装することで、好きな画像を描画できるようになる。今回は匿名クラスとしてrender()をオーバーライドしている。実際に画像を生成しているのはdrawText()メソッドで、ここではセッションを取得して入力された文字列を取り出し、java.awt.Graphicsを使用して描画を行っている。

    リスト7.10 NextPage.java

    package jp.co.ongs.example;

    import java.awt.Color;
    import java.awt.Graphics2D;

    import wicket.markup.html.WebPage;
    import wicket.markup.html.basic.Label;
    import wicket.markup.html.form.Button;
    import wicket.markup.html.form.Form;
    import wicket.markup.html.image.Image;
    import wicket.markup.html.image.resource.RenderedDynamicImageResource;

    public class NextPage extends WebPage {
            private static final long serialVersionUID = 1L;
            private static final int WIDTH = 200;
            private static final int HEIGHT = 100;

            private Label message;
            private Image image;
            RenderedDynamicImageResource resource;

            public NextPage() {
                    // ラベルを作成して内容をセット
                    this.message = new Label("message", "変換後の画像");

                    // 画像を生成
                    this.resource = new RenderedDynamicImageResource(WIDTH, HEIGHT) {
                            private static final long serialVersionUID = 1L;
                            protected boolean render(java.awt.Graphics2D aGraphics) {
                                    return drawText(aGraphics);
                            }
                    };
                    this.image = new Image("image", this.resource);

                    // フォームを作成
                    Form form = new Form("form");
                    Button backButton = new Button("back") {
                            private static final long serialVersionUID = 1L;
                            protected void onSubmit() {
                                    submit();
                            }
                    };
                    form.add(backButton);
                    
                    // 各コンポーネントをページに追加
                    super.add(this.message);
                    super.add(this.image);
                    super.add(form);
            }
            
            /**
             * backボタンを押下に割り当てられたアクション
             */
            private void submit()
            {
                    super.setResponsePage(ExamplePage.class);
            }
            
            /**
             * Graphicsにフォームから受け取ったテキストを描画する
             */
            private boolean drawText(Graphics2D aGraphics)
            {
                    // セッションを取得
                    ExampleSession session = (ExampleSession)getSession();
                    // セッションからテキストを取得
                    String text = (String)session.get(ExamplePage.TEXTAREA_ID);

                    // Graphicsを設定してテキストを描画
                    aGraphics.setColor(Color.BLACK);
                    aGraphics.setBackground(Color.WHITE);
                    aGraphics.clearRect(0, 0, WIDTH, HEIGHT);
                    if (null != text && 0 < text.length()) {
                            aGraphics.drawString(text, 0, 20);
                    }
                    
                    return true;
            }

    }

    以上で完成である。ExampleWebApplication.javaについては前回の例と同じものが使用できる。

    完成したアプリケーションをEclipseでテストすると、次のような画面遷移となる。入力した文字列に対応した画像が生成されたのが確認できる。

    まとめ

    Wicketは数あるWebアプリケーション開発のためのフレームワークの中でもとりわけ特異なものであると言える。多くのフレームワークはユーザインタフェース(HTML)やJavaプログラムとは別に、XMLなどで記述された設定ファイルを必要とする。それらのフレームワークは非常に高機能で汎用性も高いが、設定ファイルそのものが煩雑になり易いという欠点を抱えている。そこでアノテーションなどを活用した設定ファイルの自動生成機能なども盛んに実装されているが、Wicketではもっとシンプルに、設定ファイルそのものを排除することを試みている。

    もちろん、Webアプリケーション・フレームワークにはそれぞれ得手不得手があるため、一概にWicketが優れていると言い切ることはできない。状況に応じて適したフレームワークを選択することが大切である。Wicketの場合、小規模から中規模クラスのWebアプリケーションを手軽に作成するのに便利なのではないだろうか。

    なお、本稿執筆時点で公開されているWicketの最新版は「Wicket 1.2 RC1」である。Wicket 1.2ではAJAXのサポートやPortlet仕様(JSR 168)のサポートなどいくつかの新機能が追加されるほか、様々な機能の改善が行われている。また、より大規模なシステムでの使用に対応するため、いくつかの問題点の解決も図られている。Wicket 1.2の正式版は今年6月上旬にリリースされる予定となっている。

    関連記事

    新着記事

    特設サイトの情報

      求人情報

      人気記事

      一覧

      イチオシ記事

      新着記事

      特別企画

      転職ノウハウ

      あなたの仕事適性診断

      4つの診断で、自分の適性を見つめなおそう!

      Heroes File ~挑戦者たち~

      働くこと・挑戦し続けることへの思いを綴ったインタビュー

      はじめての転職診断

      あなたにピッタリのアドバイスを読むことができます。

      転職Q&A

      転職に必要な情報が収集できます

      スカウト転職する

      企業からアプローチのメッセージが届きます。

      マイナビニュースマガジン