Strutsとは、Jakarta Projectが作成している、Webアプリケーション用のフレームワークです。MVCモデル2の考え方で設計されており、StrutsはこのうちViewとControllerを担当します。Modelには、JavaBeansやEJBなど、自分の好きなものを利用できます。
Strutsを使ったWebアプリケーションは、だいたい次のような構成になります。
では、サンプルアプリケーションを見てみましょう。題材は、前章の「図書検索アプリケーション」です。
Strutsでは、JSPを使って入力フォームを作成します。タグライブラリの機能を利用する必要があるため、HTMLは使いません。
<%@ page contentType="text/html; charset=Shift_JIS" %> <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <html:html> <head> <link href="style.css" type="text/css" rel="stylesheet" /> <title>図書検索</title> </head> <body> <h1>図書検索</h1> <hr /> <html:form action="/searchBook"> <table border="1" cellpadding="5"> <tr> <td>分類番号</td> <td><html:text property="ndc" maxlength="20" size="20" /></td> </tr> <tr> <td>著者表示</td> <td><html:text property="tyosya_hyouji" maxlength="20" size="20" /></td> </tr> <tr> <td>登録番号</td> <td><html:text property="id" maxlength="20" size="20" /></td> </tr> <tr> <td>タイトル</td> <td><html:text property="title" maxlength="20" size="20" /></td> </tr> <tr> <td>著 者</td> <td><html:text property="author" maxlength="20" size="20" /></td> </tr> <tr> <td>出 版</td> <td><html:text property="publisher" maxlength="20" size="20" /></td> </tr> </table> <html:submit property="submit" value="検索" /> <html:reset value="reset" /> </html:form> </body> </html:html>
このJSPでは、"html:"からはじまるタグを多用しています。
次の部分でフォームからの入力を処理しています。html:textタグが入力欄になり、入力された文字列はproperty属性で指定したパラメータに格納されます。
「検索」ボタンが押されたら、これらのパラメータは"/searchBook"というプログラムに渡されます。これについては後述します。
<html:form action="/searchBook">
.....
<td>分類番号</td>
<td><html:text property="ndc" maxlength="20" size="20" /></td>
.....
.....
<html:submit property="submit" value="検索" />
<html:reset value="reset" />
</html>
ActionFrom Beanを作成します。このBeanには、Webブラウザからの要求のときに、クエリーとして与えられたパラメータを格納します。
ActionForm Beanは、ActionFormを継承します。そして、JavaBeansのプロパティのネーミングルールに従ってメソッドを作成します。このとき、先に示した入力フォームのパラメータ名と、このActionForm Beanのプロパティが一致している必要があります。
import java.io.UnsupportedEncodingException;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
public class SearchForm extends ActionForm {
private String ndc = "";
private String tyosya_hyouji = "";
private String id = "";
private String title = "";
private String author = "";
private String publisher = "";
public String getNdc() {
return ndc;
}
public void setNdc(String ndc) {
this.ndc = ndc;
}
public String getTyosya_hyouji() {
return tyosya_hyouji;
}
public void setTyosya_hyouji(String tyosya_hyouji) {
this.tyosya_hyouji = tyosya_hyouji;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
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 getPublisher() {
return publisher;
}
public void setPublisher(String publisher) {
this.publisher = publisher;
}
public void reset(ActionMapping mapping, HttpServletRequest req) {
try {
req.setCharacterEncoding("Shift_JIS");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
このクラスの最後にあるresetというメソッドでは、フォームからの入力した日本語が文字化けしないようにしています。HTTPでは、ブラウザから入力された文字の文字コードを判別するのは難しいため、このような処理が必要になります。このメソッドは、ActionForm Beanのそれぞれのプロパティに値が設定される前に呼び出されます。
またこのActionForm Beanを使って、必須入力項目や、入力フォーマットをチェックすることもできます。
Actionクラスは、org.apache.struts.action.Actionクラスを継承します。このクラスでは、ビジネスロジックを処理します。フォームから入力されたデータの処理なども、このクラスで行われます。
import java.util.List;
import java.util.ArrayList;
import java.io.*;
import java.sql.*;
import javax.sql.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.struts.action.*;
import jp.ac.wakhok.library.*;
public class SearchBookAction extends Action {
public ActionForward execute(
ActionMapping mapping, ActionForm f,
HttpServletRequest request, HttpServletResponse response) {
SearchForm form = (SearchForm)f;
String ndc = form.getNdc();
String tyosya_hyouji = form.getTyosya_hyouji();
String id = form.getId();
String title = form.getTitle();
String author = form.getAuthor();
String publisher = form.getPublisher();
List list =
search(ndc, tyosya_hyouji, id, title, author, publisher);
HttpSession session = request.getSession();
session.setAttribute("bookList", list);
return(mapping.findForward("success"));
}
private List search(String ndc, String tyosya_hyouji,
String id, String title,
String author, String publisher) {
List list = new ArrayList();
try {
Class.forName("org.hsqldb.jdbcDriver");
String url = "jdbc:hsqldb:hsql://localhost";
Connection con = DriverManager.getConnection(url, "sa", "");
String selectStatement =
"select * " +
"from books where ndc like ? " +
"and tyosya_hyouji like ? " +
"and id like ? " +
"and title like ? " +
"and author like ? " +
"and publisher like ? ";
PreparedStatement prepStmt =
con.prepareStatement(selectStatement);
prepStmt.setString(1, appendPercent(ndc));
prepStmt.setString(2, appendPercent(tyosya_hyouji));
prepStmt.setString(3, appendPercent(id));
prepStmt.setString(4, appendPercent(title));
prepStmt.setString(5, appendPercent(author));
prepStmt.setString(6, appendPercent(publisher));
ResultSet rs = prepStmt.executeQuery();
while (rs.next()) {
BookData book = new BookData();
book.setNdc(rs.getString("ndc"));
book.setTyosya_hyouji(rs.getString("tyosya_hyouji"));
book.setId(rs.getString("id"));
book.setTitle(rs.getString("title"));
book.setAuthor(rs.getString("author"));
book.setPublisher(rs.getString("publisher"));
list.add(book);
}
prepStmt.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return list;
}
private String appendPercent(String from) {
StringBuffer to = new StringBuffer();
to.append("%");
to.append(from);
to.append("%");
return new String(to);
}
}
この部分では、ActionForm Beanからndcプロパティを取得しています。
SearchForm form = (SearchForm)f;
String ndc = form.getNdc();
次に、データベースの検索を行い、その結果をHttpSessionに登録しています。
List list =
search(ndc, tyosya_hyouji, id, title, author, publisher);
HttpSession session = request.getSession();
session.setAttribute("bookList", list);
次の部分は、見慣れないスタイルです。
return(mapping.findForward("success"));
この"success"という文字列は、この後でどのプログラムに処理を任せるか指定するために使います。詳しくは次節で解説します。
検索された本の一覧を出力するJSPです。Strutsでは、JSPを使ってブラウザへの出力を行います。このJSPには、検索結果が収められたListが受け渡されます。Listの各要素がBookDataです。
このJSPでは、Strutsで用意されている3種類のタグライブラリを使っています。
<%@ page contentType="text/html; charset=Shift_JIS" %> <%@taglib uri="WEB-INF/struts-html.tld" prefix="html" %> <%@taglib uri="WEB-INF/struts-bean.tld" prefix="bean" %> <%@taglib uri="WEB-INF/struts-logic.tld" prefix="logic" %> <html:html> <head> <title>検索結果</title> </head> <body> <h1>検索結果</h1> <ul> <logic:iterate id="book" name="bookList" scope="session"> <li> <bean:write name="book" property="title" /> / <bean:write name="book" property="author" /> </li> </logic:iterate> </ul> </body> </html:html>
logic:iteratorタグでは、HttpSessionに格納されているCollectionあるいはIteratorをひとつずつ処理していきます。以前見たc:forEachタグと同じような働きをします。
bean:writeでは、name属性で指定されたBeanのプロパティを出力します。
アクションコンフィグレーションファイルは、Strutsのプログラムの働きを定めるファイルです。struts-config.xmlという名前のファイルになります。
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
<!-- ========== Data Source Configuration ======================== -->
<!-- ========== Form Bean Definitions ============================ -->
<form-beans>
<form-bean name="searchForm" type="SearchForm" />
</form-beans>
<!-- ========== Global Forward Definitions ====================== -->
<!-- ========== Action Mapping Definitions ====================== -->
<action-mappings>
<action path="/searchBook" type="SearchBookAction"
name="searchForm" scope="request">
<forward name="success" path="/list.jsp"/>
</action>
</action-mappings>
<!-- ========== Controller Configuration ========================= -->
<!-- ========== Message Resources Definitions ==================== -->
<!-- ========== Plug Ins Configuration =========================== -->
</struts-config>
まず、ActionForm Beanを宣言します。form-bean要素のtype属性がクラス名です。パッケージに含まれているときは、パッケージ名を忘れないようにしましょう。name属性で、このBeanに名前をつけます。
<!-- ========== Form Bean Definitions ============================ -->
<form-beans>
<form-bean name="searchForm" type="SearchForm" />
</form-beans>
次のところでは、「アクション」を定義します。アクションとは、URLで区別される処理内容のことです。
action-mappingという要素の中に、actionという要素があります。
まず、type属性がActionクラス名です。このActionクラスに対応するURLが、path属性になります。ここでは、"/searchBook.do"というパス名になります。".do"というパス名が最後につくのは、Strutsでのお約束です。
name属性はActionForm Beanです。先にform-bean要素のname属性で指定した名前がここに入ります。scope属性は、このActionForm Beanのスコープを指定します。"requst"か"session"が入ります。
<!-- ========== Action Mapping Definitions ====================== -->
<action-mappings>
<action path="/searchBook" type="SearchBookAction"
name="searchForm" scope="request">
<forward name="success" path="/list.jsp"/>
</action>
</action-mappings>
次に、action要素の子要素であるforward要素について見てみましょう。Actionクラスのところで、
return(mapping.findForward("success"));
という文がありました。ここでの"success"は、name属性の"success"と同じです。これは、"success"という文字列を返したら、次に/list.jspに処理をしてもらうことを意味します。この文字列は何でも構いませんし、複数用意しても大丈夫です。
Strutsでは、このアクションコンフィグレーションファイルを使って、ActionクラスやJSPを呼び出す順序やタイミングを定めます。アプリケーションが大規模になってActionクラスやJSPが増えても、すべてのこのアクションコンフィグレーションファイルを使ってプログラムの流れを整理するのがStrutsのコツです。
web.xmlはここで示すようなものになります。多くの場合、この設定をベースにして、必要なものを追加するのが良いでしょう。ここでは、タグライブラリなどを指定しています。
<?xml version='1.0' encoding='UTF-8'?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-app_2_4.xsd"
version="2.4">
<display-name>Struts Blank Application</display-name>
<!-- Standard Action Servlet Configuration (with debugging) -->
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>2</param-value>
</init-param>
<init-param>
<param-name>detail</param-name>
<param-value>2</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<!-- Standard Action Servlet Mapping -->
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<!-- The Usual Welcome File List -->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- Struts Tag Library Descriptors -->
<taglib>
<taglib-uri>/tags/struts-bean</taglib-uri>
<taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/tags/struts-html</taglib-uri>
<taglib-location>/WEB-INF/struts-html.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/tags/struts-logic</taglib-uri>
<taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/tags/struts-nested</taglib-uri>
<taglib-location>/WEB-INF/struts-nested.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/tags/struts-tiles</taglib-uri>
<taglib-location>/WEB-INF/struts-tiles.tld</taglib-location>
</taglib>
</web-app>
次の部分では、"do"で終わるURLは、ActionServletに処理を任せることを定義しています。
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class
......
......
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
Strutsを動かすには、まずStrutsのアーカイブのlibフォルダにあるJarファイルを、WEB-INF/libにコピーします。
さらに、タグライブラリの設定を記述している*.tldファイルをWEB-INFにコピーします。
このWebアプリケーションのファイル構成は次のようになります。
test --- search.jsp
|- list.jsp
|- style.css
|- WEB-INF/ ---- web.xml
|-- struts-config.xml
|-- struts-bean.tld
|-- struts-html.tld
|-- struts-logic.tld
|-- struts-nested.tld
|-- struts-template.tld
|-- struts-tiles.tld
|-- classes/
| |- SearchBookAction.class
| |- SearchForm.class
| |- jp/ac/wakhok/library/ --- BookData.class
|-- lib/
|- commons-beanutils.jar
|- commons-collections.jar
|- commons-digester.jar
|- commons-fileupload.jar
|- commons-lang.jar
|- commons-logging.jar
|- commons-validator.jar
|- hsqldb.jar
|- jakarta-oro.jar
|- struts.jar