開発者にやさしいIDE, NetBeansを使ってみよう!
   

第二回 NetBeansで「いきなり」開発を始めよう。(Webアプリケーション)



Java アプリケーションの開発も、近頃では、スピードの短縮と共同作業の重要 性から IDE を用いることが必須な状況になっています。中でも、無償で使える IDE ツールは非常に高い人気を獲得しています。オープンソースの Java IDE で ある NetBeans は、近頃では劇的な速度の改善と、GUI ツールやプロファイル機 能の充実度から注目度が高まり、北米では20% 近いシェアを獲得する状況になっ てきていると言われています。日本では知名度がまだ低い NetBeans ですが、今 後は、北米に追随して、ユーザ数が増えることが予想されています。
SDC では、 インストールのガイドから、適切な日本語情報の所在を示す index 的な情報を 含め、NetBeans の一通りの使用方法を解説する連載を今月よりはじめることに いたしました。本連載、そしてそこで参照されている良質のチュートリアルをご 覧いただければ、話題のJava 開発ツール、NetBeans の使用方法が一通りわかる ようになることを目標にしています。本連載は、エキスパートのリレー形式で行 う予定です。第二回目は、実際のアプリケーション開発を山本正浩氏に紹介して いただきます。

今回は、NetBeans IDEを使って、簡単なアプリケーションの開発を見ていきま す。まずは、本来あるべき統合開発環境のよさを知ってください。そして、興味 を持っていただけたなら、今度は実際に使ってみてください。NetBeansは開発の 邪魔をしません。きっとあなたの希望に応えてくれます。

NetBeansでは「いきなり」スタートできます

NetBeansは”Out-of-the-Box”で「全部入り」

NetBeansは、IDE自体がPure Java実装を貫いているということと、もう一つ大きなポリシーを持っています。それは”Out-of-the-Box”と呼ばれているもので、「インストールしたらすぐに使える」というコンセプトを表しています。つまり、開発するにあたって面倒な設定は特に必要なく、「必要なものはあらかじめ全部入っており、しかも設定済みである」ということです。

これは、統合開発環境としては実は当たり前のこととも言えるのですが、実際に体現しているIDEは多くありません。

何を作るのか考えましょう

今回は、有名なオープンソースのWebフレームワークである、Apache Strutsを使った簡単なWebアプリケーションを作成しながら、NetBeans IDEの特徴を読み解いていきたいと思います。

Strutsは、J2EEのWebフレームワークとしてはすでに枯れた技術になっています。しかし、開発現場への普及率が非常に高いため、現在もStrutsあるいはその拡張型フレームワークを使った開発が多く存在しています。NetBeansでは、このStrutsのフレームワーク開発がすぐに行えるように、Strutsのクラスライブラリの準備はもちろん、このフレームワークに特化した補助機能をいつか備えています。

開発の手順

1. MVCアーキテクチャーについて

Strutsは、MVCと呼ばれる設計思想に基づいたWebフレームワークです。今回は、簡単な蔵書検索のアプリケーションをStrutsを用いて構築していきます。MVCの利点を生かして、モデル、ビューを完全に切り離して進めていきます。

2. Webアプリケーションプロジェクトの作成

NetBeans IDE 5.0では、アプリケーションの開発は、プロジェクト単位で行います。まず、Webアプリケーションのプロジェクトを作成します。

メニューから「新規プロジェクト(W)…」を選択すると、新規プロジェクト作成ウィザードが表示されます。

図のように「Webアプリケーション」を選択し、次に進みます。

プロジェクト名を入力します。プロジェクト名は、後で変更することもできます。「WebBookshelf」と入力することにします。

プロジェクトの場所は、デフォルトではユーザーのホームディレクトリになっています。今回は、分かりやすい場所にしました。 サーバーは、NetBeansに組み込まれているバンドル版Tomcatを選択します。

フレームワークの選択では、今回Strutsを使いますので、「Struts 1.2.7」を選択します。NetBeansでは、特に追加の設定を行わなくても、JSFやStrutsを用いたWebアプリケーション開発がすぐにできるようになっています。

アプリケーションリソースは、Strutsアプリケーションで使用するリソースバンドルの名前です。

完了を選択すると、NetBeans IDEがStrutsベースのWebアプリケーションプロジェクトを生成します。

NetBeans IDEが自動的に生成したプロジェクトには、Strutsアプリケーションに必要なファイルがすべて準備された状態になっています。Javaのソースファイルは一切入っていませんが、この状態でもビルドしてWebサーバーに配備することはできます。まず、この状態で配備してみましょう。

プロジェクトを選択して、「プロジェクトを配備」を選択します。

出力ウィンドウでビルドプロセスが進行します。NetBeansのプロジェクトは、Antのプロジェクトがベースになっています。NetBeans IDE上で行う操作のほとんどはAntのビルドプロセスに関連付けられています。NetBeans IDE上で「構築」や「配備」を実行すると、NetBeansはAntのビルドプロセスを実行します。(Antを使うためには、通常build.xmlを記述する必要がありますが、NetBeans IDEはこのbuild.xmlを自動的に生成してくれますので、ユーザーがAntのスクリプトを直接編集する必要はほとんどありません)

無事配備が完了すると、NetBeans IDEはサーバーを起動します。「実行時」ウィンドウでサーバーに配備されたアプリケーションが正しく動いているか確認してみましょう。バンドル版Tomcatを開くと、「WebBookshelf」アプリケーションが配備されているのが分かります。

「ブラウザで開く」を選択すると、WebBookshelfアプリケーションを直接ブラウザで見ることができます(まだ何もありません)。

3. ビュー(JSP)の実装

まず、ビューを構成するJSPページを作成します。JSPには、アプリケーションの処理結果などを動的に反映させるためにタグライブラリやELを記述することになりますが、最初はJSP構文を含まないHTMLベースのページを作成することにしましょう。

NetBeans IDEでは、JSPページを作成するのに、実際のブラウザ表示イメージで制作することはできませんが、HTMLの要素をパレットから取り出してHTMLソースコードにする使い方ができます。HTMLの書き方を知っていれば、特にHTMLエディタなどを使わなくてもNetBeans IDEだけで済んでしまうことになります。

HTMLベースのJSPを作成したら一度ビューはストップします。

search.jsp
<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>


<%--
The taglib directive below imports the JSTL library. If you uncomment it,
you must also add the JSTL library to the project. The Add Library... action
on Libraries node in Projects view can be used to add the JSTL 1.1 library.
--%>
<%--
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 
--%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">

<html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Web bookshelf</title>
    </head>
    <body>

    <h1>Web bookshelf</h1>
    <%--
    This example uses JSTL, uncomment the taglib directive above.
    To test, display the page like this: index.jsp?sayHello=true&name=Murphy
    --%>
    <%--
    <c:if test="${param.sayHello}">
    <!-- Let's welcome the user ${param.name} -->
    Hello ${param.name}!
    </c:if>
    --%>

    <div class="head">
        <form action="search" method="POST">
        書名: <input type="text" name="title" value="" />
        著者名: <input type="text" name="author" value="" />
        カテゴリー: <select name="category">
            <option value=""></option>
            <option value="Java">Java</option>
            <option value="NetBeans">NetBeans</option>
            <option value="Linux">Linux</option>
            <option value="UML">UML</option>
        </select>
        <input type="submit" value="検索" />
        </form>
    </div>
        
    <hr />
        
    <div class="result">
        <table border="1" cellpadding="1">
            <thead>
            <tr>
                <th>書名</th>
                <th>著者名</th>
                <th>カテゴリー</th>
                <th>購入日</th>
            </tr>
            </thead>
            <tbody>
                <tr>
                    <td>UMLによる統一ソフトウェア開発プロセス</td>
                    <td>イヴァー・ヤコブソン他</td>
                    <td>UML</td>
                    <td>2002/03/21</td>
                </tr>
                <tr>
                    <td>UMLによる統一ソフトウェア開発プロセス</td>
                    <td>イヴァー・ヤコブソン他</td>
                    <td>UML</td>
                    <td>2002/03/21</td>
                </tr>
                <tr>
                    <td>UMLによる統一ソフトウェア開発プロセス</td>
                    <td>イヴァー・ヤコブソン他</td>
                    <td>UML</td>
                    <td>2002/03/21</td>
                </tr>
            </tbody>
        </table>
    </div>

    </body>
</html>

この時点で配備すると、ブラウザ上で表示を確認できます。

書名、著者名、カテゴリーを抽出の条件として、結果を下段に一覧表示する単純な構成にしたいと思います。(青字が新たに記述した箇所です)

4. データベースの確認

今回の作例では、Webアプリケーションの蔵書情報をデータベースで管理していることを想定します。NetBeans IDEでは、バージョン5.0から、JDBCドライバ経由でのデータベースアクセスが可能になっています。J2EE開発では必須の作業となるデータベースの情報の確認作業もNetBeans IDEだけで行えてしまうのです。

作例では、データベースにOracle Database 10gを使うことにします。「実行時ウィンドウ」で、データベースを展開すると、ドライバが表示されます。デフォルトでは、JDBC-ODBCブリッジのみが表示されます。今回はOracleデータベースを使いますので、右クリックしてドライバの追加を行い、OracleのJDBCドライバを追加します。使用するドライバは、実際に使うデータベース環境で提供されているものを使用してください。

ドライバが追加されたら、次は接続情報を作成します。追加したドライバを右クリックして接続を選択すると、データベースの新規接続ウィザードが表示されます。必要な情報を入力すると、接続を試みます。

表を右クリックして「データを表示」を選択すると、表の内容がIDE上で表示されます。セル上でのデータの更新などはできませんが、これもUPDATE文を作成すれば実現はできます。まさに他のツールは必要ないといった感じです。

5. モデルの実装
5.1. Bookshelfプロジェクトの作成

蔵書情報を検索するプログラムは、今回Strutsを使ってWeb画面から使いますが、検索機能自体がWebに特化している必要は全くありません。

ここでは、蔵書検索機能のビジネスロジックをStrutsと完全に切り離すために、別プロジェクトで作ってしまいましょう。こうすることで、ビルド単位を分離することができ、より確実に依存性を断ち切ることができます。

NetBeansでは、自由形式プロジェクトを選択しない限り、プロジェクトの中でコンパイルの単位を明確に分離することはできません。自由形式プロジェクトとは、NetBeansに任せることのできるAntのビルドスクリプトを手動作成し、これをNetBeansに使わせる方法です。

蔵書検索機能は、Webベースであることを無視しているので、単純に「Bookshelf」という名前でプロジェクトを作ることにしましょう。このプロジェクトは、純粋なJavaプロジェクトです。

主クラスは特に必要ないので作りません。

5.2. DTOの作成

BookShelfプロジェクトに、蔵書情報を検索する単純な仕組みを作ります。BOOKテーブルから、抽出条件に合ったレコードをリスト形式で取得する機能にします。まずは、BOOKテーブルの1レコードを表すオブジェクトを作成しましょう。

NetBeans5.5からは、テーブルからJavaのエンティティクラスを生成する機能が備わるようですが、5.0にはまだありません。少し手作業が入りますが、リファクタリング機能を使用することで比較的楽に作成することができます。

新規にJavaクラスを作成したら、BOOKテーブルの定義を元に、フィールドを定義していきます。

アクセス修飾子から全部書いても構いませんが、あとでリファクタリングにより勝手にNetBeansが整理してくれるので、最小限で済ませるなら、データ型、名称だけで大丈夫です。

さらに、NetBeans IDEのソースエディタには、随分昔からキーワードを短縮入力する機能が備わっています。Stringなら、「St」と入力してスペースキーを押すと、「String」が自動的に入力されます。細かい機能ですが、コピー&ペーストが面倒な程度のコーディング量だと手で書くことも多いので、知っていると結構素早くコーディングできます。

buydateはDate型としました。そのままだとコンパイルエラーになるので、怒られます。いくつか修正方法はありますが、今回はインポートの問題であることがすぐに判りますので、インポートの修正を使います。

Shift+Alt+Fを押すと、自動的にimport文が整理されます。Dateクラスはjava.util.Dateとjava.sql.Dateが候補にありますので、どちらを使うか聞いてきます。とりあえずより汎用的なjava.util.Dateを使います。

他のクラスからのフィールドの読み書きを可能にするために、アクセッサーメソッドを追加します。これには、リファクタリングを使用することで一気に片付けられます。

ソースエディタでクラスの中を選択状態にして、リファクタリングから「フィールドのカプセル化」を選択します。NetBeans IDEがフィールドのアクセス修飾子の変更と、getter/setterとなるメソッドの追加を提案します。プレビューウィンドウでリファクタリング実行を選択すると、カプセル化の処理が自動的に行われます。

BookInfo.java
/*
 * BookInfo.java
 *
 * Created on 2006/06/04, 17:39
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package jp.netbeans.bookshelf;

import java.util.Date;

/**
 *
 * @author yamamoto
 */
public class BookInfo {

    private String bookId;
    private String title;
    private String author;
    private String category;
    private Date buydate;
    
    /** Creates a new instance of BookInfo */
    public BookInfo() {
    }

    public String getBookId() {
        return bookId;
    }

    public void setBookId(String bookId) {
        this.bookId = bookId;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getCategory() {
        return category;
    }

    public void setCategory(String category) {
        this.category = category;
    }

    public Date getBuydate() {
        return buydate;
    }

    public void setBuydate(Date buydate) {
        this.buydate = buydate;
    }
}
5.3. Beanの作成

蔵書の抽出ロジックは、Beanクラスとして定義したいと思います。このクラスは、Bookshelfプロジェクトの呼び出し窓口として使います。WebBookshelfプロジェクトから呼び出すときも、このクラスを使うことにします。

新規にクラスを作成します。BookSearchBeanという名前にしましょう。ウィザードが自動的にソースコードのスケルトンを生成します。

BookSearchBeanに、searchBook()メソッドを定義します。パラメータは、検索条件である、書名(title)、著者名(author)、カテゴリー(category)としました。

検索に使うデータベースアクセスロジックは、DAOを用意するようにします。

まだロジックを書かないので、return nullとでもしておきましょう。

BookSearchBean.java
/*
 * BookSearchBean.java
 *
 * Created on 2006/06/04, 18:29
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package jp.netbeans.bookshelf;

import java.util.List;

/**
 *
 * @author yamamoto
 */
public class BookSearchBean {
    
    /** Creates a new instance of BookSearchBean */
    public BookSearchBean() {
    }
    
    public List<BookInfo> searchBook(String title, String author, 
        String category) {
        return null;
    }
}
5.4. DAOインターフェイスの作成

実際にデータベースにクエリーを発行する役割は、DAO(Data Access Object)というクラスにします。あとでデータベースアクセス手法を変えたいので、DAOをインターフェイスとして切り出しておきましょう。

BookDao.java
/*
 * BookDao.java
 *
 * Created on 2006/06/04, 18:32
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package jp.netbeans.bookshelf.dao;

import java.sql.SQLException;
import java.util.List;
import jp.netbeans.bookshelf.BookInfo;

/**
 * 蔵書情報のデータベースにアクセスするインターフェイスです。
 * @author yamamoto
 */
public interface BookDao {

    /**
     * データベースから蔵書情報を検索します。
     * @param title 書名
     * @param author 著者名
     * @param category カテゴリー
     * @return 蔵書情報リスト
     */
    List<BookInfo> searchBookList(String title, String author,
        String category) throws SQLException;
}
5.5. BeanにDAOを追加

BookSearchBeanは、DAOを使ってデータベースアクセスを行うようにするため、DAOを受け取る必要があります。

DAOをBookSearchBeanのフィールドに追加し、先ほどreturn nullとしたsearchBook()メソッドで、DAOを使うように修正します。リファクタリングの「フィールドのカプセル化」を再び実行して、DAOを外部からセットできるようにしましょう。

BookSearchBean.java
/*
 * BookSearchBean.java
 *
 * Created on 2006/06/04, 18:29
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package jp.netbeans.bookshelf;

import java.sql.SQLException;
import java.util.List;
import jp.netbeans.bookshelf.dao.BookDao;

/**
 *
 * @author yamamoto
 */
public class BookSearchBean {
    
    /**
     * 蔵書アクセス用DAO
     */
    private BookDao dao;
    
    /** Creates a new instance of BookSearchBean */
    public BookSearchBean() {
    }
    
    public List<BookInfo> searchBook(String title, String author, 
        String category) {
        
        List<BookInfo> bookList = null;
        try {
            bookList = this.getDao().searchBookList(title, author, category);
        } catch (SQLException e) {
            
        }
        return bookList;
    }

    public BookDao getDao() {
        return dao;
    }

    public void setDao(BookDao dao) {
        this.dao = dao;
    }
}
5.6. DAO実装クラス(JDBCコーディング版)の作成

DAOインターフェイスの実装クラスを作成します。

新規にクラスを作成し、BookDaoをimplementsします。

メニューのソースから「メソッドのオーバーライド」を選択すると、BookDaoインターフェイスに定義したメソッドが表示されるので、これを選択します。

伝統的なJDBCプログラミングによる実装としました。後で付け替える想定ですので、完全なロジックにはしていません。

DefaultBookDaoImpl.java
/*
 * DefaultBookDaoImpl.java
 *
 * Created on 2006/06/04, 18:43
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package jp.netbeans.bookshelf.dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import jp.netbeans.bookshelf.BookInfo;

/**
 *
 * @author yamamoto
 */
public class DefaultBookDaoImpl implements BookDao {
    
    /** Creates a new instance of DefaultBookDaoImpl */
    public DefaultBookDaoImpl() {
    }
    
    /**
     * コネクションを取得します。
     * @return コネクション
     * @throws SQLException コネクション取得失敗
     */
    protected Connection getConnection() throws SQLException {
        try {
            Class.forName("oracle.jdbc.driver.OracleDriver");
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        
        Connection con = DriverManager.getConnection(
                "jdbc:oracle:thin:@bloom-3:1521:bloomdb",
                "yamamoto",
                "xxxxxxxx");
        con.setAutoCommit(false);
        
        return con;
    }

    public List<BookInfo> searchBookList(String title, String author,
    	String category) throws SQLException {
        Connection con = null;
        ResultSet rs = null;
        Statement stmt = null;
        StringBuilder condition = new StringBuilder();
        if (title != null && title.length() > 0) {
            condition.append(" AND TITLE LIKE '" + title + "%'");
        }
        
        if (author != null && author.length() > 0) {
            condition.append(" AND AUTHOR LIKE '" + author + "%'");
        }
        
        if (category != null && category.length() > 0) {
            condition.append(" AND CATEGORY LIKE '" + category + "%'");
        }
        
        if (condition.length() > 0) {
            condition.replace(0, 4, "SELECT * FROM BOOK WHERE");
        } else {
            condition.append("SELECT * FROM BOOK");
        }
        
        List<BookInfo> bookList = new ArrayList<BookInfo>();
        try {
            con = this.getConnection();
            
            stmt = con.createStatement();
            rs = stmt.executeQuery(condition.toString());
            while (rs.next()) {
                BookInfo book = new BookInfo();
                book.setBookId(rs.getString("BOOK_ID"));
                book.setTitle(rs.getString("TITLE"));
                book.setAuthor(rs.getString("AUTHOR"));
                book.setCategory(rs.getString("CATEGORY"));
                book.setBuydate(rs.getDate("BUYDATE"));
                bookList.add(book);
            }
        } catch (SQLException e) {
            throw e;
        } finally {
            if (rs != null) {
                rs.close();
            }
            if (stmt != null) {
                stmt.close();
            }
            if (con != null) {
                con.close();
            }
        }
        
        return bookList;
    }
}
5.7. テストコードの作成

ここまで作ったら、とりあえず動かしたくなります。本来の順序とは異なりますが、ここでNetBeansを使ってJUnitのテストコードを作成し、実行して正しく動くかどうかを確認してみましょう。

BookSearchBeanを通したテストコードを作ってみます。プロジェクトウィンドウで、BookSearchBeanを右クリックし、ツールから「JUnitテストを作成」を選択します。NetBeansがJUnitのテストコードのスケルトンを作成してくれます。

NetBeansはテスト対象クラス(BooksearchBean)に定義されている全てのメソッドのテストケースを作成しますが、今回はsearchBook()メソッドのみを対象にするので、他のテストメソッドは削除します。

テストコードは簡単なものにしました。テストを実行してみましょう。

DAOクラスはJDBCドライバを使っているので、実行する前に、ライブラリの登録を行う必要があります。Bookshelfプロジェクトのプロパティを開いて、ライブラリのカテゴリーを選択します。ここで使用するJDBCドライバの入っているJARファイルを指定してください。

BookSearchBeanTest.java
/*
 * BookSearchBeanTest.java
 * JUnit based test
 *
 * Created on 2006/06/04, 18:55
 */

package jp.netbeans.bookshelf;

import jp.netbeans.bookshelf.dao.DefaultBookDaoImpl;
import junit.framework.*;
import java.util.List;

/**
 *
 * @author yamamoto
 */
public class BookSearchBeanTest extends TestCase {
    
    public BookSearchBeanTest(String testName) {
        super(testName);
    }

    protected void setUp() throws Exception {
    }

    protected void tearDown() throws Exception {
    }

    public static Test suite() {
        TestSuite suite = new TestSuite(BookSearchBeanTest.class);
        
        return suite;
    }

    /**
     * Test of searchBook method, of class jp.netbeans.bookshelf.BookSearchBean.
     */
    public void testSearchBook() {
        System.out.println("searchBook");
        
        String title = "UML";
        String author = "";
        String category = "";
        BookSearchBean instance = new BookSearchBean();
        instance.setDao(new DefaultBookDaoImpl());
        List<BookInfo> result = instance.searchBook(title, author, category);

        assertEquals(1, result.size());
        for (BookInfo book : result) {
            System.out.println("BOOK_ID: " + book.getBookId());
            assertEquals("000001", book.getBookId());
            System.out.println("TITLE: " + book.getTitle());
            assertEquals("UMLによる統一ソフトウェア開発プロセス", book.getTitle());
            System.out.println("AUTHOR: " + book.getAuthor());
            assertEquals("イヴァー・ヤコブソン他", book.getAuthor());
            System.out.println("CATEGORY: " + book.getCategory());
            assertEquals("UML", book.getCategory());
        }
    }
}
5.8. ActionForm Beanの作成

検索機能が一通りできあがったので、これをWebから呼び出すためにStrutsコンポーネントを作成します。まず、JSPからユーザーの入力情報をActionに通知するためのコンテナクラスを作成します。

ファイルを新規作成し、「Struts ActionForm Bean」を選択します。名前はBookSearchActionFormとします。ウィザードでActionFormを作成すると、struts-config.xmlに自動的に情報が追加されます。

struts-config.xml
<?xml version="1.0" encoding="ISO-8859-1" ?>

<!DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN"
          "http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd">


<struts-config>
    <form-beans>
        <form-bean name="BookSearchActionForm" 
      type="jp.netbeans.webbookshelf.BookSearchActionForm"/>
</form-beans> <global-exceptions> ...(以下省略)

ActionFormは、JSP画面からの入力値をActionに伝える役目を果たします。NetBeans IDEは自動的にスケルトンを作ってくれますが、これだけでは使えないので、検索条件としている3つの項目をActionFormに追加します。DTOを作ったときと同じ要領で書名、著者名、カテゴリーを追加しましょう。自動でできたフィールドとアクセッサーメソッドは削除してください。

validate()メソッドは、そのままにするとコンパイルエラーになるので、一度中身を削除しておきます。今回の作例では必須入力はないので、あとで入力チェックを追加しましょう。

BookSearchActionForm.java
/*
 * BookSearchActionForm.java
 *
 * Created on 2006/06/04, 19:56
 */

package jp.netbeans.webbookshelf;

import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionMapping;

/**
 *
 * @author yamamoto
 * @version
 */

public class BookSearchActionForm extends org.apache.struts.action.ActionForm {

    private String title;
    private String author;
    private String category;
    
    /**
     *
     */
    public BookSearchActionForm() {
        super();
        // TODO Auto-generated constructor stub
    }
    
    public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
        ActionErrors errors = new ActionErrors();
        return errors;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getCategory() {
        return category;
    }

    public void setCategory(String category) {
        this.category = category;
    }
}
6. コントローラ(Action)の実装
6.1. Actionの作成

Actionは、JSPからActionForm Beanを通してパラメータを受け取り、BookshelfプロジェクトのBookSearchBeanクラスを使って蔵書情報を検索して結果をJSPに渡すというコントーラーの役割を果たしています。

Actionの作成もNetBeans IDE上でスケルトンを生成できます。BookSearchActionという名前を付けます。「アクションのパス」は、このアクションにマッピングするURIとなるパス文字列です。今回は、「/search」としました。

ウィザードは次に、使用するActionForm Beanを聞いてきます。今回は入力元(検索画面)がJSPファイルなので、入力リソースにはsearch.jspファイルを選択します。スコープには、「要求(Request)」を選択します。

ウィザードが終了すると、Actionクラスのスケルトンが自動生成され、Formの時と同じようにstruts-config.xmlにActionのパス情報が追加されます。NetBeans IDEでは、特別なことをしない限り、struts-config.xmlなどの設定ファイルを手修正する必要はほとんどありません。

struts-config.xml
    <action-mappings>
        <action input="/search.jsp" name="BookSearchActionForm" path="/search" 
                scope="request" type="jp.netbeans.webbookshelf.BookSearchAction"/>
        <action path="/Welcome" forward="/welcomeStruts.jsp"/>
    </action-mappings>
BookSearchAction.java
/*
 * BookSearchAction.java
 *
 * Created on 2006/06/04, 20:10
 */

package jp.netbeans.webbookshelf;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForward;
/**
 *
 * @author yamamoto
 * @version
 */

public class BookSearchAction extends Action {
    
    /* forward name="success" path="" */
    private final static String SUCCESS = "success";
    
    /**
     * This is the action called from the Struts framework.
     * @param mapping The ActionMapping used to select this instance.
     * @param form The optional ActionForm bean for this request.
     * @param request The HTTP Request we are processing.
     * @param response The HTTP Response we are processing.
     * @throws java.lang.Exception
     * @return
     */
    public ActionForward execute(ActionMapping mapping, ActionForm  form,
        HttpServletRequest request, HttpServletResponse response)
        throws Exception {

        return mapping.findForward(SUCCESS);
    }
}

最後に、struts-config.xmlにフォワード情報の設定を追加します。Actionが正常に処理を終えたら、同じsearch.jspに遷移させます。

struts-config.xmlをエディタで開き、/searchパスのaction要素を選択して右クリックします。Strutsのサブメニューから、「転送を追加」を選びましょう。図のように入力して「追加」をクリックすると、struts-config.xmlにフォワード情報が書き込まれます。

struts-config.xml
    <action-mappings>
        <action input="/search.jsp" name="BookSearchActionForm" path="/search" 
            scope="request" type="jp.netbeans.webbookshelf.BookSearchAction">
            <forward name="success" path="/search.jsp"/>
        </action>
        <action path="/Welcome" forward="/welcomeStruts.jsp"/>
    </action-mappings>
6.2. Beanの呼び出しロジックを追加

NetBeans IDEが作成したBookSearchActionに、BookSearchBeanを使うロジックを追加していきます。

まず、WebBookshelfプロジェクトから、Bookshelfプロジェクトを参照できるようにします。プロジェクトのプロパティを開き、ライブラリのカテゴリーを選択します。「プロジェクトの追加」を選択して、Bookshelfディレクトリを選択します。NetBeansは、プロジェクトの参照関係を、プロジェクトのJARファイルを取り込む形で実現します。

Bookshelfプロジェクトを追加したら、BookSearchActionクラスに、デフォルト実装としたJDBC版のDAOを使ってBeanを作成し、このクラスの検索ロジックを呼び出すように処理を記述します。

BookSearchAction.java
/*
 * BookSearchAction.java
 *
 * Created on 2006/06/04, 20:10
 */

package jp.netbeans.webbookshelf;

import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jp.netbeans.bookshelf.BookInfo;
import jp.netbeans.bookshelf.BookSearchBean;
import jp.netbeans.bookshelf.dao.DefaultBookDaoImpl;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForward;
/**
 *
 * @author yamamoto
 * @version
 */

public class BookSearchAction extends Action {
    
    /* forward name="success" path="" */
    private final static String SUCCESS = "success";
    
    /**
     * This is the action called from the Struts framework.
     * @param mapping The ActionMapping used to select this instance.
     * @param form The optional ActionForm bean for this request.
     * @param request The HTTP Request we are processing.
     * @param response The HTTP Response we are processing.
     * @throws java.lang.Exception
     * @return
     */
    public ActionForward execute(ActionMapping mapping, ActionForm  form,
        HttpServletRequest request, HttpServletResponse response)
        throws Exception {
        
        BookSearchBean searchBean = new BookSearchBean();
        
        // デフォルト実装DAO(JDBC)を使う
        searchBean.setDao(new DefaultBookDaoImpl());
        
        BookSearchActionForm myForm = (BookSearchActionForm) form;
        
        // 書名、著者名、カテゴリーを指定して検索
        List<BookInfo> bookList = searchBean.searchBook(
                myForm.getTitle(), myForm.getAuthor(), myForm.getCategory());
        
        request.setAttribute("BookSearchResult", bookList);

        return mapping.findForward(SUCCESS);
    }
}
7. ビューの修正

Actionの実装が一通り終わったら、JSPの修正を行います。最初に作ったJSPは単純なHTMLなので、何も変化がありません。Actionを呼び出して結果を表示するための修正を行います。

Strutsのタグライブラリを使うために、以下の3行をJSPに追加します。

<%@ taglib uri="http://jakarta.apache.org/struts/tags-bean" prefix="bean" %>
<%@ taglib uri="http://jakarta.apache.org/struts/tags-html" prefix="html" %>
<%@ taglib uri="http://jakarta.apache.org/struts/tags-logic" prefix="logic" %>

追加すると、NetBeansのJSPエディタ上で、Strutsのタグライブラリを認識するようになり、入力候補も使えるようになります。

HTML要素、FORM要素をそれぞれ、html:html、html:formに変更します。 FORM要素のaction属性には、先ほどActionのパス文字列として入力した、「/search」を指定します。

初期画面をJSPにするので、JSPに直接アクセスした場合はパラメータを受け取れません。この対処をするため、logic:presentタグを使い、スコープにパラメータが入っている場合のみを対象に動的に変化する部分を記述します。

search.jsp
<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>


<%@ taglib uri="http://jakarta.apache.org/struts/tags-bean" prefix="bean" %>
<%@ taglib uri="http://jakarta.apache.org/struts/tags-html" prefix="html" %>
<%@ taglib uri="http://jakarta.apache.org/struts/tags-logic" prefix="logic" %>


<%--
The taglib directive below imports the JSTL library. If you uncomment it,
you must also add the JSTL library to the project. The Add Library... action
on Libraries node in Projects view can be used to add the JSTL 1.1 library.
--%>
<%--
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 
--%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">

<html:html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Web bookshelf</title>
    </head>
    <body>

    <h1>Web bookshelf</h1>
    <%--
    This example uses JSTL, uncomment the taglib directive above.
    To test, display the page like this: index.jsp?sayHello=true&name=Murphy
    --%>
    <%--
    <c:if test="${param.sayHello}">
    <!-- Let's welcome the user ${param.name} -->
    Hello ${param.name}!
    </c:if>
    --%>
    <div class="head">
        <html:form action="/search" method="POST">
        書名: <input type="text" name="title" value="" />
        著者名: <input type="text" name="author" value="" />
        カテゴリー: <select name="category">
            <option value=""></option>
            <option value="Java">Java</option>
            <option value="NetBeans">NetBeans</option>
            <option value="Linux">Linux</option>
            <option value="UML">UML</option>
        </select>
        <input type="submit" value="検索" />
        </html:form>
    </div>
        
    <hr />
        
    <div class="result">
        <logic:present name="BookSearchResult">
        <table border="1" cellpadding="1">
            <thead>
            <tr>
                <th>書名</th>
                <th>著者名</th>
                <th>カテゴリー</th>
                <th>購入日</th>
            </tr>
            </thead>
            <tbody>
            <logic:iterate name="BookSearchResult" id="book">
                <tr>
                <td><bean:write name="book" property="title" /></td>
                <td><bean:write name="book" property="author" /></td>
                <td><bean:write name="book" property="category" /></td>
                <td><bean:write name="book" property="buyDate" format="yyyy/MM/dd" /></td>
                </tr>
            </logic:iterate>
            </tbody>
        </table>
        </logic:present>
    </div>
    </body>
</html:html>
8. 配備(deploy)

お楽しみの配備です。最初にやったときと全く同じ手順です。「プロジェクトを配備」を選択すると、バンドルされているTomcatへの配備が始まります。

配備が完了したら、ブラウザで確認してみましょう。

9. 実行

正しくデータベースの内容が表示されました。少し検索条件を指定してみましょう。

正しく抽出されました。しかし・・・。

日本語でも検索してみると、どうやらちゃんと抽出されません。

10. デバッグ

とりあえずデバッグしてみることにします。まず、JSPのループ時点に一つブレークポイントを置きます。通常、疑うのはここではないのですが、最終確認もできるのであえてここに置きます。NetBeans IDEでは、当たり前のようにJSPファイル上でデバッグを実行することができます。しかも、置くだけならどこにでも置けます。

実行時ウィンドウでTomcatを右クリックし、デバッグモードで起動を選択すると、Tomcatがデバッグモードで起動し、NetBeansとの対話状態に入ります。

タグライブラリの結果を直接見られないので、リクエストスコープの中にあるListオブジェクトを取り出してサイズを見てみましょう。ウォッチポイントでJavaの式を書きます。サイズはどうやら0なので、JSPに来た時点で結果はありません。

次に、入力パラメータが正常に入っているか見てみましょう。アルファベットで検索できて日本語で検索できないのですから、文字化けを疑うのは自然な流れですね。 BookSearchActionのexecute()メソッドの中にブレークポイントを置いて、再び実行してみます。

ローカル変数ウィンドウでmyFormの中を見てみると、やはり文字化けを起こしているようです。

11. 文字化け対策

日本語で検索できないと話にならないので、文字化け対策を施します。いくつかやり方はありますが、いちばん簡単な方法は自前でエンコーディング指定をしてしまうものです。フィルターを追加する方法もあるのですが、NetBeansのバンドル版Tomcatでは(簡単に)試せないので、あえてこの方法にします。

自前でエンコーディングを変更する方法は、ActionForm Beanのreset()メソッドをオーバーライドし、リクエストオブジェクトのエンコーディング指定を変更します。

BookSearchActionFormクラスをソースエディタで開き、メニューのソースから「メソッドをオーバーライド」を選択します。reset()メソッドを選択すると、BookSearchActionFormにreset()メソッドのオーバーライドのロジックが生成されます。

せっかくオーバーライドするので、「@Override」の注釈も追加しましょう。

次のコードを入力します。今回JSPをUTF-8で作っているので、UTF-8で解釈するようにします。

request.setCharacterEncoding("UTF-8");

UnsupportedEncodingExceptionが発生するというコンパイルエラーが出ます。NetBeansはこれに対する修正提案を出してきます。今回は単純にtry/catchを選択することにします。

BookSearchActionForm.java
/*
 * BookSearchActionForm.java
 *
 * Created on 2006/06/04, 19:56
 */

package jp.netbeans.webbookshelf;

import java.io.UnsupportedEncodingException;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionMapping;

/**
 *
 * @author yamamoto
 * @version
 */

public class BookSearchActionForm extends org.apache.struts.action.ActionForm {

    private String title;
    private String author;
    private String category;
    
    /**
     *
     */
    public BookSearchActionForm() {
        super();
        // TODO Auto-generated constructor stub
    }
    
    public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
        ActionErrors errors = new ActionErrors();
        return errors;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getCategory() {
        return category;
    }

    public void setCategory(String category) {
        this.category = category;
    }

    @Override
    public void reset(ActionMapping mapping, HttpServletRequest request) {
        super.reset(mapping, request);
        try {
            request.setCharacterEncoding("UTF-8");
        } catch (UnsupportedEncodingException ex) {
            ex.printStackTrace();
        }
    }
}

修正が完了したら、再度配備して試してみましょう。今度は大丈夫なようです。

NetBeans IDEには、HTTPモニターというユニークな機能が備わっています。実はこの文字化け問題は、このHTTPモニターを見ると一発で判ります。確かに文字化けしていますね。

修正前
修正後

終わり

いかがでしたか?チュートリアルベースですが、少し手の込んだ内容でNetBeansでの開発をご紹介しました。ここまでの手順は、他のツールを一切使うことなく、NetBeansのみで行いました。また、NetBeansにプラグインを入れることもありませんでした。かゆいところに手の届かない部分はまだまだありますが、NetBeansだけで全て行えてしまうところにこのIDEにすごさがあります。

記事には含めることができませんでしたが、DAOの実装にiBATISを用いたソースコードも用意しました。NetBeansでは、外部のライブラリを用いた開発も非常に簡単に行うことができます。ちゃんと配備に至るまで面倒を見てくれます。今回配備に用いるWARファイルの作成に関して説明しなかったのは、NetBeansがすべて裏でやっていて説明する必要がなかったからです。

リリースが待ち望まれているNetBeans 5.5では、Java EE5ベースのアプリケーションがNetBeansだけで行えてしまいます。今から早速触ってみませんか?