|
|
||
Simon Brown's BlogJ2EE ArchivesComparing webapp frameworks : WebWorkPosted by simongbrown on March 24, 2006 at 03:02 PM | Permalink | Comments (5)Like Struts, WebWork is a framework that is fairly established within the J2EE webapp space although it's interesting that I've only ever come across two types of WebWork users - those that have never heard of it and those that love it. WebWork, like most other frameworks, is designed around the web MVC pattern and uses the command and controller implementation strategy. What's slightly different about WebWork is that it's built on top of XWork, a separate framework that provides an implementation of the command pattern that is independent of the Servlet API.
In XWork, actions (or commands, depending on your terminology) implement the
Right, let's get on with seeing how a WebWork implementation of the sample application compares to the others. I'm using WebWork 2.1.7 and installing it is a simple matter of copying a few JAR files into the <servlet> <servlet-name>webwork</servlet-name> <servlet-class>com.opensymphony.webwork.dispatcher.ServletDispatcher</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>webwork</servlet-name> <url-pattern>*.action</url-pattern> </servlet-mapping> <taglib> <taglib-uri>webwork</taglib-uri> <taglib-location>/WEB-INF/lib/webwork-2.1.7.jar</taglib-location> </taglib>
Two additional configuration files that we need are called <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.dtd">
<validators>
</validators>
The other file ( Home page The home page presents the user a list of recent blog entries and with WebWork, the code that finds these blog entries get wrappeds up inside an XWork action as follows. package action;
import com.opensymphony.xwork.ActionSupport;
import domain.Blog;
import domain.BlogService;
/**
* Action responsible for finding blog entries, ready to be displayed.
*
* @author Simon Brown
*/
public class ViewBlogEntriesAction extends ActionSupport {
/** the blog that owns the blog entries */
private Blog blog;
/**
* Performs the processing associated with this action.
*
* @return a String defining the outcome of this action
*/
public String execute() throws Exception {
BlogService blogService = new BlogService();
this.blog = blogService.getBlog();
return SUCCESS;
}
/**
* Gets the blog that this action is operating upon.
*
* @return a Blog instance
*/
public Blog getBlog() {
return blog;
}
}
This implementation is very straightforward, just looking up the <!DOCTYPE xwork PUBLIC "-//OpenSymphony Group//XWork 1.0//EN"
"http://www.opensymphony.com/xwork/xwork-1.0.dtd">
<xwork>
<include file="webwork-default.xml" />
<package name="default" extends="webwork-default">
<default-interceptor-ref name="defaultStack" />
<action name="viewBlogEntries" class="action.ViewBlogEntriesAction">
<result name="success" type="dispatcher">viewBlogEntries.jsp</result>
</action>
</package>
</xwork>
Here, an action called <%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib uri="webwork" prefix="ww" %>
<%@ taglib uri="http://java.sun.com/jstl/fmt_rt" prefix="fmt" %>
<!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><ww:property value="blog.name" /></title>
<link rel="stylesheet" href="screen.css" type="text/css" />
</head>
<body>
<div id="container">
<h1><ww:property value="blog.name" /></h1>
<h2><ww:property value="blog.description" /></h2>
<ww:iterator id="blogEntry" value="blog.blogEntries">
<div class="blogEntry">
<h3><ww:property value="title"/></h3>
<ww:if test="excerpt != null">
<ww:property value="excerpt"/>
<p>
<a href="<ww:url value="'viewBlogEntry.action'">
<ww:param name="'id'" value="id"/>
</ww:url>">Read more</a>
</p>
</ww:if>
<ww:else>
<ww:property value="body"/>
</ww:else>
<p>
Posted on <ww:property value="date" />
</p>
</div>
</ww:iterator>
</div>
</body>
</html>
If you're familiar with something like the Struts bean/logic tags or JSTL, you'll probably pick up the WebWork tags pretty quickly.
Blog entry detail page package action;
import com.opensymphony.xwork.ActionSupport;
import domain.Blog;
import domain.BlogEntry;
import domain.BlogService;
/**
* Responsible for finding a specific blog entry.
*
* @author Simon Brown
*/
public class ViewBlogEntryAction extends ActionSupport {
/** the ID of the blog entry to display */
private String id;
/** the blog that owns the blog entries */
private Blog blog;
/** the blog entry to be displayed */
private BlogEntry blogEntry;
/**
* Performs the processing associated with this action.
*
* @return a String defining the outcome of this action
*/
public String execute() throws Exception {
BlogService blogService = new BlogService();
blog = blogService.getBlog();
blogEntry = blog.getBlogEntry(id);
if (blogEntry == null) {
return "notfound";
} else {
return SUCCESS;
}
}
/**
* Setter for the id property.
*
* @param id a String
*/
public void setId(String id) {
this.id = id;
}
/**
* Gets the blog that this action is operating upon.
*
* @return a Blog instance
*/
public Blog getBlog() {
return blog;
}
/**
* Gets the blog entry to be displayed.
*
* @return a BlogEntry instance
*/
public BlogEntry getBlogEntry() {
return blogEntry;
}
}
Next up is the fragment of XML that needs to be inserted into the <action name="viewBlogEntry" class="action.ViewBlogEntryAction">
<result name="success" type="dispatcher">viewBlogEntry.jsp</result>
<result name="notfound" type="dispatcher">404.jsp</result>
</action>
And finally is the JSP page itself. <%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib uri="webwork" prefix="ww" %>
<%@ taglib uri="http://java.sun.com/jstl/fmt_rt" prefix="fmt" %>
<!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><ww:property value="blogEntry.title" /> : <ww:property value="blog.name" /></title>
<link rel="stylesheet" href="screen.css" type="text/css" />
</head>
<body>
<div id="container">
<h1><ww:property value="blog.name" /></h1>
<h2><ww:property value="blog.description" /></h2>
<div class="blogEntry">
<h3><ww:property value="blogEntry.title" /></h3>
<ww:property value="blogEntry.body" />
<p>
Posted on <ww:property value="blogEntry.date" />
</p>
</div>
</div>
</body>
</html>
Summary Comparing webapp frameworks : WicketPosted by simongbrown on March 09, 2006 at 01:53 AM | Permalink | Comments (3)Guillermo Castro has posted a Wicket implementation of the webapp comparison that I started a while ago. It's an interesting read and the contrast with most page/request based webapp frameworks is amazing. In summary, Guillermo says: Wicket, in my opinion, focuses the development efforts in the right place, inside plain Java code, and leaves the graphical presentation where it should be, inside html. At first you might find a little hard to grasp this paradigm shift, as so many developers are being 'forced' to rely on jstl and jsp scriptlets to accomplish logic programming for a page with all the other frameworks. But once you get used to this, I'm sure wicket will provide really fast development times.
From my perspective, the thing that I really like about Wicket is that way that you can build common components, as illustrated by the I've never seen a finer explanation of why Java web development takes forever. One of the reasons that I wanted to compare frameworks with a simple read-only app is because I wanted to see whether the easy stuff was actually easy, and I've taken a lot of flak about this. From a newcomers perspective, Wicket *looks* complicated. Once I (eventually!) get a few more frameworks written up, I'll add some read/write behaviour into the application and hopefully we'll start to see some of Wicket's strengths shine through. My thanks go to Guillermo for volunteering to do this. I have the WebWork and Stripes versions half written up, so I'll make an effort to getting these out as soon as possible. If anybody would like to volunteer to provide a similar implementation in other webapp frameworks, please get in touch and I'll e-mail you the source. If you don't have a website/blog, I'm more than happy to publish it on my blog for you. Comparing 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
Comparing webapp frameworks : Model 1 with scriptletsPosted by simongbrown on November 23, 2005 at 11:42 AM | Permalink | Comments (1)Before we dive into the frameworks, I want to drop back to basics to give some context behind why the frameworks exist and what benefits they provide. For this reason, let's look at a naive model 1 implementation of the sample application. If you're already familiar with the whole model 1 vs. model 2 thing, you might want to skip reading this particular entry.
Overview
Home page
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ page import="domain.*,
java.util.Iterator,
java.text.DateFormat" %>
<%
BlogService blogService = new BlogService();
Blog blog = blogService.getBlog();
%>
<!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.getName() %></title>
<link rel="stylesheet" href="screen.css" type="text/css" />
</head>
<body>
<div id="container">
<h1><%= blog.getName() %></h1>
<h2><%= blog.getDescription() %></h2>
<%
Iterator it = blog.getBlogEntries().iterator();
while (it.hasNext()) {
BlogEntry blogEntry = (BlogEntry)it.next();
%>
<div class="blogEntry">
<h3><%= blogEntry.getTitle() %></h3>
<%
if (blogEntry.getExcerpt() != null) {
%>
<%= blogEntry.getExcerpt() %>
<p>
<a href="viewBlogEntry.jsp?id=<%= blogEntry.getId()%>">Read more</a>
</p>
<%
} else {
%>
<%= blogEntry.getBody() %>
<%
}
%>
<p>
<%
DateFormat dateFormat = DateFormat.getDateTimeInstance(
DateFormat.LONG, DateFormat.LONG, blog.getLocale());
dateFormat.setTimeZone(blog.getTimeZone());
%>
Posted on <%= dateFormat.format(blogEntry.getDate()) %>
</p>
</div>
<%
}
%>
</div>
</body>
</html>
I'm not going to explain each and every line, but the basic structure breaks down as follows.
For the "read more" link, this is simply implemented as a call to the blog entry detail JSP page, passing the blog entry ID as a parameter. Notice how this is conditionally included, depending on whether the excerpt is null - it's a good example of the simple presentation logic that you'll find in many web applications. The date formatting code is another example.
Blog entry detail page
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ page import="domain.*,
java.text.DateFormat" %>
<%
BlogService blogService = new BlogService();
Blog blog = blogService.getBlog();
BlogEntry blogEntry = blog.getBlogEntry(request.getParameter("id"));
if (blogEntry == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
%>
<!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.getTitle() %> : <%= blog.getName() %></title>
<link rel="stylesheet" href="screen.css" type="text/css" />
</head>
<body>
<div id="container">
<h1><%= blog.getName() %></h1>
<h2><%= blog.getDescription() %></h2>
<div class="blogEntry">
<h3><%= blogEntry.getTitle() %></h3>
<%= blogEntry.getBody() %>
<p>
<%
DateFormat dateFormat = DateFormat.getDateTimeInstance(
DateFormat.LONG, DateFormat.LONG, blog.getLocale());
dateFormat.setTimeZone(blog.getTimeZone());
%>
Posted on <%= dateFormat.format(blogEntry.getDate()) %>
</p>
</div>
</div>
</body>
</html>
The scriptlet at the top of the page is probably the most interesting because that piece of code is responsible for locating the requested blog entry or sending the user off to an error page, which is simply configured through the <error-page> <error-code>404</error-code> <location>/404.jsp</location> </error-page>
Summary
Comparing webapp frameworks : Domain modelPosted by simongbrown on November 09, 2005 at 04:21 AM | Permalink | Comments (1)
Before we kick off our look at webapp frameworks, let's establish the domain model we're working with. It's pretty simple - blogs and blog entries, residing in a package called
Because I want to concentrate on the presentation aspects of the frameworks this time around, I'm ignoring the persistence mechanism. Instead, I'm just assuming that data access will be achieved via a "service", regardless of whether it uses direct JDBC, a DAO, a web service, Hibernate, etc. So that's the model. The question is, will any of the frameworks or view technologies require us to change it? Comparing webapp frameworks : RequirementsPosted by simongbrown on November 04, 2005 at 05:45 AM | Permalink | Comments (8)So, to compare webapp frameworks we need an example web application. I've chosen to build a simple blog. Here are the functional requirements.
And here are some non-functional requirements, just to make things a little more interesting.
Here are some screenshots to show what this all looks like.
Figure 1 : Home page.
Figure 2 : Blog entry detail page.
Figure 3 : Page not found page. As I've said before, I hoping to make this an iterative process and there's plenty of scope for adding some interactivity into the example application. Next up is a look at the domain model. Comparing webapp frameworks : Why?Posted by simongbrown on November 03, 2005 at 11:59 AM | Permalink | Comments (11)After posting Comparing webapp frameworks : Introduction yesterday, I got lots of feedback - some good, some bad. So, why am I doing this? First off, here are some of the negative comments.
Fantastic! At least people are talking and I got some response. Great to see that the community is alive. :-)
Choice
Nobody cares about the simple stuff
Consistency and reduced learning curve
The other thing to pick up on here is that some of the framework sites do have incredibly simple examples. They're even simpler than the application I've come up with and that's a problem. Once you stray outside of the simple example you have to start digging around the docs. If I can at least go slightly deeper than the very simple examples, again, I think this adds value.
The evolution of web MVC
Just to clarify, my sample application is read-only from the perspective of the user. The content is dynamic, but the user can't update it. As I said, I am hoping to make this an iterative process and revisit it to add further levels of detail. I've got to start somewhere though. ;-) Comparing webapp frameworks : IntroductionPosted by simongbrown on November 02, 2005 at 12:21 PM | Permalink | Comments (9)Struts, WebWork, Stripes, Spring MVC, Wicket, Tapestry, JSF, etc, or even rolling your own. With so many J2EE web application frameworks to choose from, how do you decide which one to use? Several articles (e.g. JavaServer Faces vs Tapestry) and presentations (e.g. Comparing Web Frameworks) already exist, but they generally concentrate on a small subset of the available frameworks. Over the next few weeks, I'm going to implement a small web application using as many J2EE web application frameworks that I can get my hands on. I'll be using the resulting code to compare and contrast what the frameworks provide and how they work. Clearly this is a massive task so, to reduce the scope, I'm going to focus on what it takes to build a read only web application. If I were to hazard a guess, I'd say that the 80-20 rule applies. 80% of a web application is read only and 20% is interactive (e.g. HTML forms, AJAX, etc). Of course, this is changing with technologies like AJAX, but we're still on the upward curve. Traditionally, that 20% is the most complex and is an area where many web application frameworks claim their unique selling points. For this reason, I may iterate over the evaluation process to take into account how the frameworks help web developers build interactive webapps. For now, I'm going to look at whether the frameworks make doing the 80% easy. In addition to looking at the webapp frameworks, I'm also going to compare and contrast some of the view technologies that are currently available. Here, I'm talking about JSP (normal and XML formats), Velocity, FreeMarker, etc. I think this adds an interesting twist and your choice of view technology may have an impact on your framework decision. Stay tuned, it should be an interesting journey! I have ServletsPosted by simongbrown on July 14, 2005 at 09:07 PM | Permalink | Comments (3)In Got Servlets?, Greg is asking what we'd like to see in the next major revision of the Java Servlets specification. In no particular order, here are my initial thoughts.
On a more general note, admittedly I've not used Java 5 annotations much, but I'm one of these people that is a little bit skeptical of them. They do have their uses, but I am a little concerned about them being overused, for example, to configure components through the code. Mike Spille has a great blog entry named Action Hippo Rangers! where he talks about the ping pong nature of software development. One of the examples he cites is the adoption of annotations versus XML configuration. The point is, just because XML configuration is seen as complex and verbose, this doesn't mean that we should switch everything over to annotations. Everything in moderation, otherwise we'll be campaigning for the return of the web.xml file in a couple of years! ;-) Where are all the J2EE 1.4 implementations?Posted by simongbrown on November 02, 2004 at 12:24 PM | Permalink | Comments (4)It's still relatively early days for J2EE 1.4 in the real world, but compare the number of J2EE 1.4 implementations (6) with the number of J2EE 1.3 implementations (21). I remember that prior to the final J2EE 1.3 release, vendors seemed to be falling over themselves to get their implementations out but this doesn't seem to be happening anymore. Most J2EE app servers have some ability to support web services (the key addition for J2EE 1.4) so I'd be surprised if the lack of implementations is due to the complexity involved. Even JSP 2.0 and Servlet 2.4 containers were fairly thin on the ground until recently. What do you think? Message acknowledgment and redelivery with message-driven beansPosted by simongbrown on June 03, 2004 at 02:23 PM | Permalink | Comments (3)We've been doing some prototyping over the past week and wanted to put together a simple enterprise application to prove some of the characteristics of JMS message processing via message-driven beans. The application server we're using is WebSphere Application Server 5.1 and for the purposes of our testing, we've been using the embedded messaging server. Although WSAD isn't my favourite tool, it does allow us to develop/deploy/test components incredibly quickly and this is great for tweaking the properties of MDB's to see how they behave differently. One of the features that we've been playing with is the transaction demarcation on MDBs. As with session beans, you can use container-managed transactions (CMT) or bean-managed transactions (BMT). However, unlike session beans, you don't seem to be able to easily implement a functionally identical bean with CMT and BMT. It comes down to message acknowledgment.
If you have a CMT message-driven bean with the transaction attribute defined as Moving on to BMT message-driven beans and things are not the same. Of course, it's the responsibility of the bean developer to create and start a transaction, but a side-effect of this is that the acknowledgment of the JMS message isn't contained within this transaction. The following paragraph from section 15.4.7, Transaction context of message-driven bean methods, of the EJB 2.0 specification does in fact make this explicit. When a message-driven bean using bean-managed transaction demarcation uses the javax.transaction.UserTransaction interface to demarcate transactions, the message receipt that causes the bean to be invoked is not part of the transaction. If the message receipt is to be part of the transaction, container-managed transaction demarcation with the Required transaction attribute must be used. If you think about the sequence of events and when the transaction is started, this does make sense. Unfortunately, in the next section the spec goes on to say the following. Message-driven beans should not attempt to use the JMS API for message acknowledgment. Message acknowledgment is automatically handled by the container. If the message-driven bean uses container managed transaction demarcation, message acknowledgment is handled automatically as a part of the transaction commit. If bean managed transaction demarcation is used, the message receipt cannot be part of the bean-managed transaction, and, in this case, the receipt is acknowledged by the container.
So then, message acknowledgment is handled by the container. With CMT, a commit or rollback will effectively acknowledge or not acknowledge the message, but with BMT the message will always be acknowledged. This is a subtle point but means with BMT that if the The reason that we discovered this is because we were trying to figure out the best way to implement logic like, "sorry, not quite ready to process this message yet". With CMT, rolling back the transaction puts the message back on the queue ready for it to be delivered to another MDB instance and if it wasn't for the listener ports in WebSphere Application Server blowing up, we might have had a solution. Now we're exploring other options, including the usual mechanism of forwarding on the message to another (or the same) queue. We're also looking into our WebSphere listener port problem and hopefully it's just a config issue. ;-) Inconsistency between Servlet specification implementationsPosted by simongbrown on November 27, 2003 at 03:49 AM | Permalink | Comments (3)I’ve been playing around with the various implementations of the security features in Servlet 2.3 compatible web containers. If you’ve ever built secure web applications, you’ll know that there are a handful of fairly useful methods on the HttpServletRequest interface. For example, the getUserPrincipal() method allows you to get access to the authenticated principal, as described by the following Javadoc. Returns a java.security.Principal object containing the name of the current authenticated user. If the user has not been authenticated, the method returns null.
The reason that I've been looking into all this in detail is that I'm trying to get my open source blogging software (Pebble) to work consistently between vendor implementations. In the webapp, I have a collection of unsecured pages and a collection of secured pages that are defined to fall within a security constraint that is declared in the
For example, on Tomcat and Resin, a call to the This issue first came up because a few people have deployed my blogging webapp onto JBoss and were reporting that they could not see some of the "admin" links that appear on the top of every page after logging in. Having delved through the Servlet specification and accompanying Javadoc, I'm still not sure who has correctly implemented this particular feature. A workaround to this is fairly straightforward, and just requires that the user's credentials are saved away in the session, using something like the following:
// some J2EE web containers don't allow programmatic access to the
// principal information from resources that don't fall under a
// security constraint - for this reason this information is placed into
// the user's session
AuthenticatedUser user = new AuthenticatedUser();
user.setName(request.getUserPrincipal().getName());
user.setBlogOwner(request.isUserInRole(Constants.BLOG_OWNER_ROLE));
user.setBlogContributor(request.isUserInRole(Constants.BLOG_CONTRIBUTOR_ROLE));
request.getSession().setAttribute(Constants.AUTHENTICATED_USER, user);
Regardless of the workaround, this does present some interesting questions. Which implementation is correct? What happens when the specification is unclear? Are the Javadocs considered as a part of the specification? After all, they are a programming contract and included in the specification itself. In this case, my opinion is that Tomcat and Resin are correct because the important point is that the user has been authenticated. What do you think, and what do you think would happen if all the implementations were put through the J2EE compatibility tests? Deploying to a certified implementation is my next step and should guarantee consistency. Perhaps making the compatibilty test kits free to open source implementations would help in situations like this, removing doubt as to what is correct. Panther ships with Ant, XDoclet and JBossPosted by simongbrown on October 28, 2003 at 07:53 AM | Permalink | Comments (4)I've just installed Panther and since you don't get stuff like CVS installed by default, I decided to open up the XCode CD and install the developer tools. To my surprise there are some Java tools tucked away including Ant, XDoclet, log4j and JBoss. If you install the Java tools, they can be found in :
In Apple's inevitable way, running JBoss is as simple as navigating to the I knew that the server editions of Panther were going to include JBoss, but not the normal editions. Of course, you can already get newer versions of the tools yourself, but it's nice to see Apple making this stuff available and pushing Java as one of their development platforms. It just enables people to get up and running quickly, meaning that they can concentrate on learning how to develop applications rather than setting up tools. I wonder if we can convince Microsoft to include J2SE and Tomcat in the next edition of their OS? ;-) XDoclet in the J2EE web tierPosted by simongbrown on October 16, 2003 at 03:18 AM | Permalink | Comments (10)It's common that you'll find somebody using XDoclet to help build their EJBs, but how often do you find people using it to help with the J2EE web tier? In his recent weblog, Dave says he is trying to convince his team that using XDoclet is the way to go for generating artefacts like tag library descriptors and the web.xml file. I must admit that while XDoclet is very useful when building EJBs, I'm not sold on the idea of using for the web tier myself. Why? Well, I think that there are several reasons for this. For each EJB, you have to build 3 classes and the ejb-jar.xml file - all of which can be automated with XDoclet and a single source file representing the bean implementation. This is nice because the remembering all that nasty EJB deployment descriptor stuff is a pain, and everybody knows it's a pain. In one of the JavaOne sessions that I went to this year, the EJB team talked about how they were thinking of taking advantage of the new J2SE 1.5 (Tiger) feature of annotations/metedata (JSR-175) so that the deployment descriptors were automatically generated. Basically this would make XDoclet-like, attribute-oriented programming available to the J2EE platform, and that can't be a bad thing. Another reason to use XDoclet is so that package and class names are kept in sync between the Java source and the deployment descriptors. However, when you use tools like IntelliJ to refactor a class/package name, you can ask it to look in other files too so you get the deployment descriptor refactoring for free. I do like using XDoclet for generating the EJB stuff, but for me it doesn't add much for the web tier technologies. Are you using XDoclet in your J2EE apps? I'd be interested to hear from anybody with thoughts on this. Integrating Tomcat and Apache on Mac OS XPosted by simongbrown on October 13, 2003 at 02:24 PM | Permalink | Comments (3)I've been looking at integrating Apache and Tomcat on my PowerBook so that my dev environment more closely matches the box hosting my domain. Although I really do like open source, one of the biggest problems for me is that I always seem to need software that I have to build from the original source. This is one of the reasons I bought a Mac. I have tried running some of the various Linux distros (e.g. RedHat and Mandrake) and every bit of my hardware seems to need drivers and kernels recompiled. Not my idea of fun! Anyway, after a quick Google, I came across an article on the MacDevCenter entitled Integrating Tomcat with Apache Via the mod_jk Module. Admittedly it is slightly out of date with respect to the versions of Tomcat and mod_jk available, but the instructions do work and it has a link to download a prebuilt version of mod_jk for Mac OS X. Five minutes is all this took to get running and I didn't even have to recompile anything! File access in EJBPosted by simongbrown on October 08, 2003 at 11:23 AM | Permalink | Comments (5)File access has always been a controversial activity within EJB-based applications because of the restrictions placed upon bean providers by the EJB specification. The part of the specification relevant here is under the section entitled Programming Restrictions, and it states the following about accessing the filing system. An enterprise bean must not use the java.io package to attempt to access files and directories in the file system. This is a fairly specific statement, and is followed up by a short explanation of why this is the case. The file system APIs are not well-suited for business components to access data. Business components should use a resource manager API, such as JDBC, to store data. While this explanation highlights a key reason for not using file I/O, I think that there is much more to this. However, although this is a well known restriction, actually finding more information on this is a time consuming task. So, in the quest for knowledge, I did some digging and came up with the following reasons why file I/O is "a bad thing"TM.
So then, how are we supposed to access files from EJB? Many people advocate the use of an intermediary Java class to wrap up the file access, believing that the EJB specification only disallows access from the bean class itself. Is this true? I'm not convined because all the same reasons apply. The specification itself presents an answer, and that answer is to use a resource manager so that we can treat file access as a secure, transactional, pooled resource. One such implementation is a J2EE Connector Architecture (JCA) adapter that you write, deploy and configure to access your filing system. In fact, some vendors have already built JCA adapters that access flat files and these are particularly useful if you have to access the outputs of legacy, mainframe systems. Of course, many types of file access can be worked around. For example, configuration information can be placed in LDAP, JNDI, a database, or even properties files delivered inside your JAR files that get loaded as a resource through the classloader. In those circumstances where accessing files is a requirement, then other solutions include loading the file through the servlet container, having it sent to the EJB tier via messaging, downloading the file from a webserver through a socket connection and so on.
These are all workarounds for the programming restriction but at the end of the day I think you have to be pragmatic. Many projects do utilise file access from within the EJB tier and their solutions work. Although the EJB specification imposes a restriction, in reality many vendors choose not to enforce this, meaning that using the How do you test tag libraries?Posted by simongbrown on October 07, 2003 at 11:58 AM | Permalink | Comments (6)Matt is looking for a way to test tag libraries and rather than write a lengthy comment, I thought I'd follow it up here. In the first part of his post, Matt says, ... I've looked briefly at TagUnit, but aren't you just writing JSPs (with custom tags) to test JSPs? In answer to this question, yes, you're exactly rght in saying that you're just writing JSPs. This is the essence of TagUnit, and why I believe it's a better way to test tags than using something like Cactus. Sure, you can test the various classes with JUnit, but since you don't use the classes in this way from a JSP page, this approach can only take you so far. JSP tags are reusable components and I think that they should be tested as such. For me, this means testing those tags in the same way that they will be used on a JSP page, and that's by writing the tests and assertions as JSP pages. Doing this has several benefits. First of all, you don't have to start mocking out parts of the servlet specification just to test the tag handlers. Instead, you're running the tests inside a real server. If you've looked into the JSP specification, you'll know that the tag lifecycle is a tricky business because of the way that JSP containers can optionally reuse and pool tag handler instances. By writing your tests as JSP pages, you instantly get all this framework for free, meaning that you can accurately test the behaviour on your desired target platform. Also, if you're aiming for cross-container compatibility, this is as simple as (in theory!) just dropping the WAR file containing the tests into a new container. Matt wants an Ant/JUnit solution and I can see why this is desirable. I'm far more likely to run the tests if I don't have to keep starting up the JSP container. Unfortunately, there aren't many ways around this without exploring avenues such as mock objects. However, TagUnit does provide an Ant task that can be used to initiate and collect the results of running the JSP-based tests. The basics of this are documented, although this document is being expanded as we speak ... isn't it Sam? ;-) As for whether there are HTML versions of the docs ... not yet, but there might be one day. I should probably add a disclaimer at this point, and highlight the fact that I created the TagUnit project. Clearly I'm biased, but I do think that it provides an improved way to test custom tags. Whether it's better ... that's for you to decide. Installing Tomcat 4 as an NT servicePosted by simongbrown on September 26, 2003 at 03:57 AM | Permalink | Comments (18)While Matt was talking about the Tomcat Service Manager, coincidentally, I was trying to install Tomcat 4 as a service on a Windows 2000 machine yesterday. For various reasons, I didn't want to install Tomcat via the .exe and therefore had to hunt around for the command line that installs the NT service. For (my) future reference, here it is, split over several lines for readability. set JAVA_HOME=c:\j2sdk1.4.2_01 set CATALINA_HOME=c:\jakarta-tomcat-4.1.27 %CATALINA_HOME%\bin\tomcat -install "Jakarta Tomcat 4" %JAVA_HOME%\jre\bin\server\jvm.dll -Djava.class.path=%CATALINA_HOME%\bin\bootstrap.jar;%JAVA_HOME%\lib\tools.jar -Dcatalina.home=%CATALINA_HOME% %CATALINA_OPTS% -Xrs -start org.apache.catalina.startup.Bootstrap -params start -stop org.apache.catalina.startup.Bootstrap -params stop -out %CATALINA_HOME%\logs\stdout.log -err %CATALINA_HOME%\logs\stderr.log Professional JSP, 3rd Edition (Apress) to be released soonPosted by simongbrown on August 06, 2003 at 08:09 AM | Permalink | Comments (4)Well ... it looks like our work on Professional JSP, 3rd Edition (previously titled Professional JSP 2.0, and now to be published by Apress) is almost at an end. It's currently slated for a September release and Amazon is now listing it, albeit with an incorrect authors list. I imagine that Sam, Dave, Matt and the other authors are looking forward to this being released as much as I am. If you've not seen it, the JSP 2.0 specification provides some great new features that I think will really change the way that JSP-based web applications are developed in the future. Finally we have a native JSP expression language and this makes building templated pages much easier, without leaving the JSP environment. Another big improvement is the introduction of tag files and simple tags. These are both ways to easily build custom tags with JSP syntax or Java code respectively. Here's an early excerpt from one of my chapters back when in the days when it was going to be published by Wrox. There's some great new stuff coming our way and I can't wait for Tomcat 5 to become rolled out and production ready. | ||
|
|