« 2019年7月 | トップページ | 2019年9月 »

2019年8月15日 (木)

Apache Wicketを使ってみる

0. 事の始まり

ある筋から「Apache Wicketを学習しといて」とのお達しがきたので自分向けメモ。

1. Apache Wicketとは

公式の記載をざっくり集約するとこんなところ。

  • 2000年代半ばのJava Webアプリケーションフレームワーク戦争の、数少ない生き残りの一つ(2004年に世に出た)
  • Apacheソフトウェア財団のもと、Apache 2.0 Licenseで公開されているOSS
  • Ajax対応機能を内包
  • 多言語対応(デフォルトで25言語を内包)
  • ページやコンポーネントはJavaオブジェクトとして扱える
  • 複数タブ/ウィンドウへの対応
  • ページやコンポーネントのテストのサポート
  • CDIやSpring、GuiceなどのDIをサポート
  • JPA、Bean ValidationなどのJava EE 6サポート
  • 最新安定版のWicket 8では、Java SE 8の言語仕様に対応するため、Java 8とServlet API 3.1が必須に

まぁ、最後のやつは、いまどきならよほどのことがない限りクリアできていると思うけど。
※そのよほどの(ry

2. セットアップ

プロジェクト自体はMavenで管理可能。
※言うまでもないが、Maven自体の扱いについては割愛。

MavenのコマンドラインはQuick Start Wizardで作成できる。

このページで生成されたコマンドラインをターミナルに投入すれば、Mavenが必要なものをそろえてくれるんだが、Windows使いで、PowerShellを使うときには注意が必要。

PS > mvn archetype:generate `
>>   "-DarchetypeGroupId=org.apache.wicket" `
>>   "-DarchetypeArtifactId=wicket-archetype-quickstart" `
>>   "-DarchetypeVersion=8.5.0" `
>>   "-DgroupId=com.mycompany" `
>>   "-DartifactId=myproject" `
>>   "-DarchetypeRepository=https://repository.apache.org/" `
>>   "-DinteractiveMode=false"

といった具合にmvnの -D オプションをダブルクォーテーションで囲わないと、「出力ディレクトリがわからない」とMavenに怒られる。
ハイフンで始まる部分がPowerShellのオプションと解釈されてしまうのが、どうも原因なようだ。なお、DOS窓なら問題なし。

Mavenがプロジェクトを生成したら、mvn clean installで必要ライブラリをダウンロードする。このあたりは、ほかのMavenプロジェクトと同じ扱い。

あとはIDEにMavenプロジェクトとしてインポートすれば、開発体制はとりあえず整う。
必要があれば、愛用のIDEにプラグインを入れてやろう。

3. 開発サーバの起動

生成されたpom.xmlをみると、dependencyブロックにJetty 9が含まれているので、

PS > mvn jetty:run

で localhost:8080 でJettyが起動する。

4. ページの構成など

Strutsなんかに代表されるクラシックなJava Webアプリケーションと違い、ViewとなるHTMLと、処理を受け持つJavaクラス(≒バッキングビーン)が1対1対応していて、ともに同じJavaパッケージ階層に置く必要がある。

このあたり、どちらかといえばASP.NETに近いかも。ただし、CSSなどを駆使して描画する枠線などは、独自タグのあるASP.NETと違い、普通にCSSで定義する。以下、GitHubにあるexampleから引用。

<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
<head>
  <title>Wicket Examples - sample panel</title>
</head>
<body>
Everything outside of the <wicket:border> tags will be ignored.
Might be handy as preview code.

  <wicket:border>
    <div style="border: 2px dotted #fc0; width: 400px; padding: 5px;">
      before the border contents <br />
    <wicket:body/>
    <br />after the border contents <br />
    </div>
  </wicket:border>

</body>
</html>

JSPとは違い、HTMLにカスタムタグを埋め込む形式なので、普通にプレビューできるのはいいと思う。

サブミットの処理については、バッキングビーンにViewのformに対応するインナークラスを定義して処理するそうな。
以下、こちらもGitHubにある公式のexampleから引用。

public final class GuestBook extends WicketExamplePage {
  /** A global list of all comments from all users across all sessions */
  private static final List<Comment> commentList = new ArrayList<>();

  /**
   * Constructor that is invoked when page is invoked without a session.
   */
  public GuestBook() {
    // Add comment form
    add(new CommentForm("commentForm"));

    // Add commentListView of existing comments
    add(new PropertyListView<>("comments", commentList) {
      @Override
      public void populateItem(final ListItem listItem) {
        listItem.add(new Label("date"));
        listItem.add(new MultiLineLabel("text"));
      }
    }).setVersioned(false);
  }

  /**
   * A form that allows a user to add a comment.
   *
   * @author Jonathan Locke
   */
  public final class CommentForm extends Form<ValueMap> {
    /**
     * Constructor
     *
     * @param id The name of this component
     */
    public CommentForm(final String id) {
      // Construct form with no validation listener
      super(id, new CompoundPropertyModel<>(new ValueMap()));

      // this is just to make the unit test happy
      setMarkupId("commentForm");

      // Add text entry widget
      add(new TextArea<>("text").setType(String.class));

      // Add simple automated spam prevention measure.
      add(new TextField<>("comment").setType(String.class));
    }

    /**
     * Show the resulting valid edit
     */
    @Override
    public final void onSubmit() {
      ValueMap values = getModelObject();

      // check if the honey pot is filled
      final String _comment = (String) values.get("comment");
      if (_comment != null && !_comment.isBlank()) {
        error("Caught a spammer!!!");
        return;
      }
      // Construct a copy of the edited comment
      Comment comment = new Comment();

      // Set date of comment to add
      comment.setDate(new Date());
      comment.setText((String)values.get("text"));
      commentList.add(0, comment);

      // Clear out the text component
      values.put("text", "");
    }
  }

  /**
   * Clears the comments.
   */
  public static void clear() {
    commentList.clear();
  }
}

5. Ajax対応について

サブミット処理の応用。処理を受け付けるクラスの中で、AjaxFallbackLinkで発動用のリンクと紐づけ、AjaxRequestTargetに処理結果を渡す。

このしくみだと、JavaScriptでイベントハンドラをゴリゴリ書かなくてもAjax対応できるのはいいかもしれない。

6. データアクセス

Wicket自体にはデータアクセス用のコンポーネントはないので、Spring Dataなんかと組み合わせる必要がある。

7. 雑感

今回やったところとしてはこんなところ。

  • Viewがあるページには、普通にHTMLのプレビューがきくので、画面の作成はJSPを使うものより効率よくできそう
  • Ajaxをつかった動きのあるページの作成は、比較的簡単に作れそう

認証とテストについては、また日を改めて挑むことにしよう。

| | コメント (0)

« 2019年7月 | トップページ | 2019年9月 »