|
|
||
Simon Brown's BlogJanuary 2006 ArchivesComparing webapp frameworks : StrutsPosted by simongbrown on January 26, 2006 at 09:48 AM | Permalink | Comments (4)Struts is the grandaddy of Java webapp frameworks so it's fitting that we start our tour here. I think it's probably safe to say that Struts was the first model 2 (web MVC) framework to gain widespread adoption in the Java arena and to this day it's still used by many people. Just to ensure everybody is up to speed, model 2/web MVC is an architectural pattern that promotes separation of concerns between the model, the view and the controller in a web environment. As we saw in the previous model 1 implementations of the sample application, each of the JSP pages contains Java code to lookup data and display it to the user. With an MVC approach, each of the components has a strict responsibility as follows.
In reality, different people have different views as to whether the controller represents only the web specific parts of the process flow and whether real business logic in fact resides in the model (it being a model of the underlying business). What's important here is that web MVC promotes a separation of concerns through reusable and testable components, regardless of how you cut it. For more information about the model 2 architecture, take a look at Designing Web Applications and Servlet Patterns.
Home page package action;
import domain.Blog;
import domain.BlogService;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ViewBlogEntriesAction extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws Exception {
BlogService blogService = new BlogService();
Blog blog = blogService.getBlog();
request.setAttribute("blog", blog);
return mapping.findForward("success");
}
}
In Struts terminology, this class is called an action although you'll also see this general pattern referred to as the command pattern. At runtime, the Struts controller (a Java servlet) maps the incoming request onto an action class and calls the <%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title><bean:write name="blog" property="name" /></title>
<link rel="stylesheet" href="screen.css" type="text/css" />
</head>
<body>
<div id="container">
<h1><bean:write name="blog" property="name" /></h1>
<h2><bean:write name="blog" property="description" /></h2>
<logic:iterate id="blogEntry" name="blog" property="blogEntries" >
<div class="blogEntry">
<h3><bean:write name="blogEntry" property="title" /></h3>
</div>
<logic:notEmpty name="blogEntry" property="excerpt">
<bean:write name="blogEntry" property="excerpt" filter="false" />
<p>
<html:link action="viewBlogEntry" paramId="id" paramName="blogEntry" paramProperty="id">Read more</html:link>
</p>
</logic:notEmpty>
<logic:empty name="blogEntry" property="excerpt">
<bean:write name="blogEntry" property="body" filter="false" />
</logic:empty>
<p>
Posted on <bean:write name="blogEntry" property="date" format="dd MMM yyyy HH:mm:ss" />
</p>
</logic:iterate>
</div>
</body>
</html>
As you can see, moving the Java code out of the page makes for a shorter JSP that looks just like regular XHTML, albeit with a few custom tags here and there. On that note, I've chosen to use Struts tags in this version of the page purely to show something a little different to last time, but you have other alternatives including the (more feature rich) JSTL tags.
So how does a request for the home page make its way through to the <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>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
Next, the mapping between a URL and the Java class responsible for processing that request is specified in a file called <action-mappings>
<action path="/viewBlogEntries" type="action.ViewBlogEntriesAction" name="viewBlogEntries">
<forward name="success" path="/viewBlogEntries.jsp" />
</action>
<action path="/viewBlogEntry" type="action.ViewBlogEntryAction" name="viewBlogEntry">
<forward name="success" path="/viewBlogEntry.jsp" />
<forward name="notfound" path="/404.jsp" />
</action>
</action-mappings>
Within each of the action mappings, you'll also notice one or
Blog entry detail page package action;
import domain.Blog;
import domain.BlogEntry;
import domain.BlogService;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ViewBlogEntryAction extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws Exception {
BlogService blogService = new BlogService();
Blog blog = blogService.getBlog();
request.setAttribute("blog", blog);
BlogEntry blogEntry = blog.getBlogEntry(request.getParameter("id"));
if (blogEntry == null) {
return mapping.findForward("notfound");
} else {
request.setAttribute("blogEntry", blogEntry);
return mapping.findForward("success");
}
}
}
No big surprises here except that there are two possible routes through the action depending on whether the specified blog entry is found or not. And here's the JSP page used to render the successful response. <%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title><bean:write name="blogEntry" property="title" /> : <bean:write name="blog" property="name" /></title>
<link rel="stylesheet" href="screen.css" type="text/css" />
</head>
<body>
<div id="container">
<h1><bean:write name="blog" property="name" /></h1>
<h2><bean:write name="blog" property="description" /></h2>
<div class="blogEntry">
<h3><bean:write name="blogEntry" property="title" /></h3>
<bean:write name="blogEntry" property="body" filter="false" />
<p>
Posted on <bean:write name="blogEntry" property="date" format="dd MMM yyyy HH:mm:ss" />
</p>
</div>
</div>
</body>
</html>
Summary Comparing webapp frameworks : Model 1 with JSP XMLPosted by simongbrown on January 12, 2006 at 02:49 AM | Permalink | Comments (0)For completeness, I wanted to show how the JSP pages from the JSTL version could be written using the JSP XML syntax. Unsurprisingly, many people don't even know of its existence and, as I've blogged before, there aren't that many situations where you'd want to use it to write pages by hand. Should you need to, here are a couple of examples of how you would go about it using the XML syntax.
Home page
<?xml version="1.0" encoding="UTF-8"?>
<jsp:root xmlns="http://www.w3.org/1999/xhtml"
xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:c="http://java.sun.com/jstl/core_rt"
xmlns:fmt="http://java.sun.com/jstl/fmt_rt"
version="2.0">
<jsp:useBean id="blogService" scope="request" class="domain.BlogService"/>
<c:set var="blog" value="${blogService.blog}" />
<jsp:output doctype-root-element="html"
doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"
doctype-system="http://www.w3c.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"/>
<jsp:directive.page contentType="text/html;charset=UTF-8" />
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>${blog.name}</title>
<link rel="stylesheet" href="screen.css" type="text/css" />
</head>
<body>
<div id="container">
<h1>${blog.name}</h1>
<h2>${blog.description}</h2>
<c:forEach var="blogEntry" items="${blog.blogEntries}">
<div class="blogEntry">
<h3>${blogEntry.title}</h3>
<c:choose>
<c:when test="${not empty blogEntry.excerpt}">
${blogEntry.excerpt}
<p>
<a href="viewBlogEntry.jsp?id=${blogEntry.id}">Read more</a>
</p>
</c:when>
<c:otherwise>
${blogEntry.body}
</c:otherwise>
</c:choose>
<p>
Posted on <fmt:formatDate value="${blogEntry.date}"
timeZone="${blog.timeZone}" type="both"
dateStyle="long" timeStyle="long" />
</p>
</div>
</c:forEach>
</div>
</body>
</html>
</jsp:root>
In comparison to the previous implementation, the major difference here is that the JSP page must be well formed XML, with everything wrapped up inside a
Blog entry detail page
<?xml version="1.0" encoding="UTF-8"?>
<jsp:root xmlns="http://www.w3.org/1999/xhtml"
xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:c="http://java.sun.com/jstl/core_rt"
xmlns:fmt="http://java.sun.com/jstl/fmt_rt"
version="2.0">
<jsp:output doctype-root-element="html"
doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"
doctype-system="http://www.w3c.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"/>
<jsp:directive.page contentType="text/html;charset=UTF-8" />
<jsp:directive.page import="domain.*" />
<jsp:scriptlet>
BlogService blogService = new BlogService();
Blog blog = blogService.getBlog();
request.setAttribute("blog", blog);
BlogEntry blogEntry = blog.getBlogEntry(request.getParameter("id"));
if (blogEntry == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
} else {
request.setAttribute("blogEntry", blogEntry);
}
</jsp:scriptlet>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>${blogEntry.title} : ${blog.name}</title>
<link rel="stylesheet" href="screen.css" type="text/css" />
</head>
<body>
<div id="container">
<h1>${blog.name}</h1>
<h2>${blog.description}</h2>
<div class="blogEntry">
<h3>${blogEntry.title}</h3>
${blogEntry.body}
<p>
Posted on <fmt:formatDate value="${blogEntry.date}"
timeZone="${blog.timeZone}" type="both"
dateStyle="long" timeStyle="long" />
</p>
</div>
</div>
</body>
</html>
</jsp:root>
Summary
Right, let's put model 1 behind us and start comparing some webapp frameworks. :-) Comparing webapp frameworks : Model 1 with JSTLPosted by simongbrown on January 10, 2006 at 09:05 AM | Permalink | Comments (2)It's been a while since the last blog entry, but let's continue our look at the webapp frameworks with another model 1 implementation, this time using the JavaServer Pages Standard Tag Library (JSTL). In Comparing webapp frameworks : Model 1 with scriptlets we saw that model 1 applications typically have some boilerplate code at the top of the page to "set the scene" and the use of scriptlets means short pieces of Java code scattered throughout the page to control the presentation logic. As an alternative to this, we could use the JSTL.
Home page
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jstl/fmt_rt" prefix="fmt" %>
<jsp:useBean id="blogService" scope="request" class="domain.BlogService"/>
<c:set var="blog" value="${blogService.blog}" />
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>${blog.name}</title>
<link rel="stylesheet" href="screen.css" type="text/css" />
</head>
<body>
<div id="container">
<h1>${blog.name}</h1>
<h2>${blog.description}</h2>
<c:forEach var="blogEntry" items="${blog.blogEntries}">
<div class="blogEntry">
<h3>${blogEntry.title}</h3>
<c:choose>
<c:when test="${not empty blogEntry.excerpt}">
${blogEntry.excerpt}
<p>
<a href="viewBlogEntry.jsp?id=${blogEntry.id}">Read more</a>
</p>
</c:when>
<c:otherwise>
${blogEntry.body}
</c:otherwise>
</c:choose>
<p>
Posted on <fmt:formatDate value="${blogEntry.date}"
timeZone="${blog.timeZone}" type="both"
dateStyle="long" timeStyle="long" />
</p>
</div>
</c:forEach>
</div>
</body>
</html>
As you can see, the boilerplate code at the top of the page to locate the
Blog entry detail page
<%@ page import="domain.*" %>
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jstl/fmt_rt" prefix="fmt" %>
<%
BlogService blogService = new BlogService();
Blog blog = blogService.getBlog();
request.setAttribute("blog", blog);
BlogEntry blogEntry = blog.getBlogEntry(request.getParameter("id"));
if (blogEntry == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
} else {
request.setAttribute("blogEntry", blogEntry);
}
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>${blogEntry.title} : ${blog.name}</title>
<link rel="stylesheet" href="screen.css" type="text/css" />
</head>
<body>
<div id="container">
<h1>${blog.name}</h1>
<h2>${blog.description}</h2>
<div class="blogEntry">
<h3>${blogEntry.title}</h3>
${blogEntry.body}
<p>
Posted on <fmt:formatDate value="${blogEntry.date}"
timeZone="${blog.timeZone}" type="both"
dateStyle="long" timeStyle="long" />
</p>
</div>
</div>
</body>
</html>
Summary
| ||
|
|