いまさらServletとJSPで作るWebアプリケーション

Javaで言うWebフレームワークと言うとちょっと前に流行ったStrutsと言うのがある。 これはここ数年で開発主体がStruts2へと主流が移り、またVersion 1系がEnd Of Lifeになったりと、 状況が大きく動いている。

今回はJSFやStruts2をはじめ乱立するWebフレームワークを置いておいて、 ServletJSPへ回帰してみた話。

実際にServletベースで小規模なアプリケーションを開発したが、 思いのほかいい感じだったのでWebアプリケーションの フレームワークに必要な要素などを再考してみました。

余談:個人的にはコントローラの記述はSeasar ProjectのSAStrutsが一番好きですが、 JSPのタグライブラリがStruts1ベースなんだよね。あのタグライブラリあんまり好きじゃない。JerseyのViewableクラスもいっそのことJ2EE標準にすれば良いのに。

と言う所で惜しい物は一杯あるけど、これだ!と言う決定版がない。誰か良い物を知っていたら教えてください。

今回のAPの構成

APの構成は下記。

Persistence層の技術にHibernateを使用しているが それ以外の層は素朴なJava環境にて構築した。

メリット

とにかく枯れている

Javaが存在する限りサポートされ続けるであろう基盤技術。

Webベースの基本知識を流用可能

素朴なWebアプリケーションの知識をそのまま転用可能。

JSFWicketなど最近のライブラリではWebの機能を大きくラップしているため、 独自の技術を獲得する必要あり。

モデルビューコントローラとWeb Frameworkが果たすべき機能

生産性の高いビュー開発技術

生産性の高いビューの開発技術はWebアプリケーションを構築する上では必須となる。 JSPやVelocity等がここに位置する技術となる。 また、Wicketではコンポーネントベースの考え方によってビューを開発する。

JSPは単体では十分な生産性を持つとは言い難いが、 JSTL(JSP Standard Tag Library)を組み込むことで生産性を向上させる事が出来る。

ビューとコントローラの分離

モデルの分離はWebアプリケーションに限らない 多くのアプリケーション共通のテーマであると考えるため、 あえてWebフレームワークの基本機能とは考えない。

モデルとコントローラの分離は ビジネスロジック層、サービス層の技術をもって分離する。

実はServletはRequestDispatcherを使用する事で、 コンテキスト内の任意のコンテンツに転送したり、 任意のコンテンツをインクルードする事が可能。

今回はこの技術をコアにしてJSPによるビューと Servletによるコントローラの分離を行った。

まとめ

要するにロジックはJavaでUIはJSPで書ければ良いと考える。

また、画面と画面遷移を分離できることも重要な要素。

実装例

ログインのサブミットボタンのアクションで実装例。 Servletはアクションのみ処理し、 エラーならログイン画面へ戻し、 ログイン成功すればメニューへ転送している。

public class LoginSubmitServlet extends HttpServlet 
{
    private static final long serialVersionUID = 1L;
    
	@Override
	protected void doPost(
			HttpServletRequest request, 
			HttpServletResponse response)
		throws ServletException, IOException 
	{
		String userName = request.getParameter("userName");
		String password = request.getParameter("password");
		
		AuthenticationService service = Services.getAuthenticationService();
		
		ArrayList errors = new ArrayList();
		Authentication auth = null;
		try {
			auth = service.authenticate(userName, password);

			if(auth == null) {
				errors.add("The user name or password is unmatch.");
			}
		} catch(Exception ex) {
			errors.add(ex.getMessage());
		}
		
		if(errors.size() > 0) {
			request.setAttribute(
					"messages", 
					errors.toArray(new String[0]));
			request.setAttribute("userName", userName);
			request.setAttribute("password", password);
						
			forwardToLogin(request, response);
			return;
		}
		
		HttpSession session = request.getSession(true);
		session.setAttribute(Constants.AUTH_SESSION_ATTR, auth);
		
		response.sendRedirect(request.getContextPath() + Constants.APP_ROOT_PATH);
	}

	private void forwardToLogin(
			ServletRequest request, 
			ServletResponse response) 
		throws IOException, ServletException 
	{
		// ログイン画面へフォワード
		request.getRequestDispatcher("/WEB-INF/jsp/login.jsp")
			.forward(request, response);
	}
}

/WEB-INF/jsp/login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" isELIgnored="false"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!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"/>
<link rel="stylesheet" type="text/css" href="<c:url value="/o/style/rssreader.css"/>"/>
<title>ログイン</title>
</head>
<body>
	<jsp:include page="/WEB-INF/jsp/base/header.jsp"/>
	<jsp:include page="/WEB-INF/jsp/base/menu-nologin.jsp"/>
	<div id="content">
		<h3>ログイン</h3>
		
		<form method="post" action="<c:url value="/o/login-submit"/>">
			<table class="form">
				<tr>
					<th>メールアドレス:</th>
					<td><input type="text" name="userName" value="<c:out value="${userName}"/>"/></td>
				</tr>
				<tr>
					<th>パスワード:</th>	
					<td><input type="password" name="password" value="<c:out value="${password}"/>"/></td>
				</tr>
				<tr>
					<td colspan="2">
						<input type="submit" name="login" value="ログイン"/>
					</td>
				</tr>
			</table>
		</form>
		
		<c:if test="${messages != null}">
			<div class="messagebox">
				<ul>
					<c:forEach var="message" items="${messages}">
						<li><c:out value="${message}"/></li>
					</c:forEach>
				</ul>
			</div>
		</c:if>
	</div>
	<jsp:include page="/WEB-INF/jsp/base/footer.jsp"/>
</body>
</html>