Groovy 応用: Struts への透過的な適用

2011.3.23 (鈴)

1. はじめに

Groovy 入門 §2.4 では,Groovy 言語で記述し groovyc でコンパイルしたクラスが Java 言語によるクラスと全く同じように Java から使えることを示した。 ここでは,より実用に即した例として Struts による Web アプリケーションに透過的に Groovy を適用できることを示そう。

ここで「透過的」とは一体どんなことなのかは読んでみてのお楽しみです。

実験に使うソフトウェアは次のとおりとする。

Groovy 1.7.10 のインストール方法は Groovy 入門 §2.2 を参考にされたい。 本稿では Tomcat のインストールから説明する。

以下,Mac OS X 10.5.8 での操作例を記述するが,他の Unix/Linux でも特に変わらない。 Windows では startup.sh の代わりに startp.bat を使うなど適宜読み替えられたい。

2. Struts による Web アプリケーションの準備

適当なディレクトリで apache-tomcat-6.0.32.tar.gz を展開する。 その bin ディレクトリにある statup.sh を実行するとバックグラウンドで Tomcat が起動する。 起動に使った端末はこの後すぐに閉じてもよい。

$ tar xf apache-tomcat-6.0.32.tar.gz
$ cd apache-tomcat-6.0.32/bin
$ ./startup.sh
記述を簡単にするため,これ以降どれもホーム・ディレクトリの直下に展開したと仮定して説明します。 あまりほめたやりかたではありませんから,実際には適当なディレクトリの下を選んで適宜読み替えて実験してください。

適当なディレクトリで struts-1.3.10-all.zip を展開する。

$ unzip struts-1.3.10-all.zip

展開して得られた apps 下の struts-cookbook-1.3.10.war を Tomcat の webapps ディレクトリにコピーする。 Tomcat が起動中ならば,コピーされた war ファイルの内容が自動的に展開され Web アプリケーションとして配置される。 もとの struts-1.3.10 ディレクトリはこれ以降不要だから削除してもよい。

$ cd struts-1.3.10/apps
$ cp struts-cookbook-1.3.10.war ~/apache-tomcat-6.0.32/webapps/
$ ls -F ~/apache-tomcat-6.0.32/webapps
ROOT/				manager/
docs/				struts-cookbook-1.3.10/
examples/			struts-cookbook-1.3.10.war
host-manager/
$ cd ../..
$ rm -r strtus-1.3.10

Tomcat が起動しているマシンの 8080 番ポートの /struts-cookbook-1.3.10/ を Web ブラウザで開いて確認する。

$ open http://localhost:8080/struts-cookbook-1.3.10/

下記のような画面が見られる。Struts による Web アプリケーションを試すことができる。


Tomcat を停止するには shutdown.sh を実行する。

$ cd ~/apache-tomcat-6.0.32/bin
$ ./shutdown.sh

3. Groovy の透過的な適用

Groovy の透過的な適用を可能にするため, Web アプリケーションの WEB-INF/lib ディレクトリに Groovy の組込み用 jar ファイル groovy-all-1.7.10.jar をコピーする。

$ cd ~/apache-tomcat-6.0.32/webapps/struts-cookbook-1.3.10/WEB-INF/lib/
$ cp ~/groovy-1.7.10/embeddable/groovy-all-1.7.10.jar .
とりあえず用意はこれですべて終わりです。環境変数の設定云々の面倒ごとは不要です。

準備した Web アプリケーションの最初の例 "Simple Form using ActionForm" に Groovy を適用しよう。対象となるソースファイルは次の二つである。

$ cd ../src/java/
$ ls examples/simple/
ProcessSimpleAction.java	SimpleActionForm.java

この二つのファイルを groovyc でコンパイルし,結果を WEB-INF/classes/ に置く。 コンパイルするファイルは struts-core-1.3.10.jar に依存しているからクラスパスに含める。

$ groovyc -d ../../classes -cp ../../lib/struts-core-1.3.10.jar examples/simple/*

これにより,WEB-INF/classes/examples/simple/ にあった ProcessSimpleAction.classSimpleActionForm.class が, groovyc でコンパイルされたファイルに置き換わる。

このとき確かに Struts に対して Groovy を適用しているが, 外見上は groovy-all-1.7.10.jar ファイルを一つ増やしただけであり, ソースファイルの接尾辞さえ変えていない。透過的な適用であるといってよい。

4. もっと Groovy らしく

もちろん,このままでは Java のかわりに Groovy を使った意味がない。 Groovy の利点として Java に比べ簡潔な表現ができることが挙げられる。 下記は examples/simple/SimpleActionForm.java を Groovy らしく書き直した例である。

/*
 * $Id: SimpleActionForm.java 471754 2006-11-06 14:55:09Z husted $
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package examples.simple;

import javax.servlet.http.HttpServletRequest;

import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;

/**
 * A simple ActionForm
 *
 * @version $Rev: 471754 $ $Date: 2006-11-06 08:55:09 -0600 (Mon, 06 Nov 2006) $
 * Groovyfied: h23-03-22 by (鈴)
 */
public class SimpleActionForm extends ActionForm {

    // ------------------------------------------------------ Instance Variables

    /** Name */
    String name = null;

    /** Secret */
    String secret = null;

    /** Color */
    String color = null;

    /** Confirm */
    boolean confirm = false;

    /** Rating */
    String rating = null;

    /** Message */
    String message = null;

    /** Hidden */
    String hidden = null;

    // ------------------------------------------------------------ Constructors

    /**
     * Constructor for MultiboxActionForm.
     */
    public SimpleActionForm() {
        super();
    }

    // ---------------------------------------------------------- Public Methods

    /**
     * Reset all properties to their default values.
     *
     * @param mapping The mapping used to select this instance
     * @param request The servlet request we are processing
     */
    public void reset(ActionMapping mapping, HttpServletRequest request) {
        this.name = "Oki";
        this.secret = null;
        this.color = null;
        this.confirm = false;
        this.rating = null;
        this.message = null;
        this.hidden = null;
    }

    /**
     * Validate the properties that have been set from this HTTP request,
     * and return an <code>ActionMessages</code> object that encapsulates any
     * validation errors that have been found.  If no errors are found, return
     * <code>null</code> or an <code>ActionMessages</code> object with no
     * recorded error messages.
     *
     * @param mapping The mapping used to select this instance
     * @param request The servlet request we are processing
     *
     * @return ActionMessages if any validation errors occurred
     */
    public ActionErrors validate(
        ActionMapping mapping,
        HttpServletRequest request) {

        ActionErrors errors = new ActionErrors();

        // Name must be entered
        if (! name) {
            errors.add("name", new ActionMessage("errors.name.required"));
        }

        // Secret Phrase must be entered
        if (! secret) {
            errors.add("secret", new ActionMessage("errors.secret.required"));
        }

        return errors;
    }
}

ここで利用した Groovy の特長は次の二つである (Groovy 入門 §3 参照)。

加えて,間違いなく groovyc でコンパイルしたファイルが動作していることを確かめるため, reset メソッドでわざと name フィールドのデフォルト値をある特定の文字列にした。

        this.name = "Oki";

groovyc でコンパイルした後,新しいクラスファイルを Tomcat に読み込ませるため, apache-tomcat-6.0.32/binshutdown.shstartup.sh を続けて実行して Tomcat を再起動する。

$ groovyc -d ../../classes -cp ../../lib/struts-core-1.3.10.jar examples/simple/*.java
$ ~/apache-tomcat-6.0.32/bin/shutdown.sh
Using CATALINA_BASE:   /Users/suzuki/apache-tomcat-6.0.32
Using CATALINA_HOME:   /Users/suzuki/apache-tomcat-6.0.32
Using CATALINA_TMPDIR: /Users/suzuki/apache-tomcat-6.0.32/temp
Using JRE_HOME:        /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home
Using CLASSPATH:       /Users/suzuki/apache-tomcat-6.0.32/bin/bootstrap.jar
$ ~/apache-tomcat-6.0.32/bin/startup.sh
Using CATALINA_BASE:   /Users/suzuki/apache-tomcat-6.0.32
Using CATALINA_HOME:   /Users/suzuki/apache-tomcat-6.0.32
Using CATALINA_TMPDIR: /Users/suzuki/apache-tomcat-6.0.32/temp
Using JRE_HOME:        /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home
Using CLASSPATH:       /Users/suzuki/apache-tomcat-6.0.32/bin/bootstrap.jar
$

それから再びブラウザで http://localhost:8080/struts-cookbook-1.3.10/ を開いて,最初の例 "Simple Form using ActionForm" を実行する。 下記のような画面を見ることができる。


first name のデフォルト値として妥当かどうかはさておき,name フィールドに該当する欄に確かにある特定の文字列が入っていることを確認してください。 間違いなく Groovy 化したファイルが動いています。

5. おわりに

本稿では Struts を例にとって,Java プロジェクトへの Groovy の採用が技術的に容易であることを示した。 基本的には次の二つを行えばよい。

  1. groovy-all-*.*.*.jar をクラスパスに入れる。
  2. javac のかわりに groovyc を使う。

このとき,さしあたり Groovy 処理系のコンパイラとしての側面だけを使うことができる。 動的なインタープリタとしての機能はこれだけでは使えないが,それでも,コードの簡潔な表記や (この例では使わなかったが) クロージャなどを,将来のバージョンの Java を待つことなく今,利用できる。


目次へ


Copyright © 2011 OKI Software Co., Ltd.