How to call domain object methods from JSP
Series [ Calling domain objects from JSPs ] Tags [ controller, domain object method, DTO, Java, JSP, JSP method calls, MVC, software design, taglib ]
It seems like the earlier post about super() spawned at least a little (offline) discussion, so here’s another comparison of some different ways of approaching a problem. People should definitely feel free to post some comments , lend your opinions, offer up code snippets, etc. Ok, so here’s the deal. We have a middleware domain object that has a method with arguments, and we essentially want to call that method from a JSP page. To make this concrete, let’s suppose we have some software supporting a blog. Here’s the middleware for a blog post:
public class Post {
public String getTopic() { /* ... */ }
public List<Comment> getCommentsByRange(int first, int max) { /* ... */ }
}
So, we let’s say we have a web page where we want to show, for
example, the first eight comments under the post itself. Probably the
JSP page will want to, somehow, essentially call
post.getCommentsByRange(0,8)
.
Approach A
Call from controller, add to model. Our controller will look like this (assuming Spring MVC).
public class PostController implements Controller {
public ModelAndView handleRequest(...) {
Map model = new HashMap();
String postId = request.getParameter("postId");
Post post = postDao.getPost(postId);
List<Comment> comments = post.getCommentsByRange(0, 8);
model.put("post", post);
model.put("comments", comments);
return new ModelAndView(..., model);
}
}
And our jsp page will contain the following snippet:
<c:foreach items="${model['comments']}" var="comment">
<!-- display a Comment in here -->
</c:forEach>
Pros:
- No extra classes or taglibs needed.
Cons:
- Presentation/business logic appears in the controller, which is arguably not its job.
- Controller gets really messy the more items like this have to get added to the model.
Approach B
Create a data transfer object. This creates a special class whose methods take no arguments:
public class PostDTO {
private Post post;
private List<Comment> comments;
public PostDTO(Post post, int start, int max) {
this.post = post;
this.comments = post.getCommentsByRange(start,max);
}
public getTopic() { return post.getTopic(); }
public getComments() { return comments; }
}
Now the controller looks like as below. Notice that there’s only one object put into the model, so the controller is very simple.
public class PostController implements Controller {
public ModelAndView handleRequest(...) {
Map model = new HashMap();
String postId = request.getParameter("postId");
Post post = postDao.getPost(postId);
PostDTO postDTO = new PostDTO(post, 0, 8);
model.put("postDTO", postDTO);
return new ModelAndView(..., model);
}
}
Finally, the JSP snippet looks like:
<c:foreach items="${model['postDTO'].comments}" var="comment">
<!-- display a Comment in here -->
</c:forEach>
Pros:
- Standard JSP.
- Controller stays simple, and does not contain presentation logic.
Cons:
- We have to create this extra DTO class which doesn’t do anything particularly interesting.
Approach C
Use a taglib. I’m not going to show the code here (want to keep
to my self-imposed time limit), but essentially, we create a taglib
that knows how to call the getCommentsByRange()
method of a
post. The JSP would look like:
<c:set var="comments" value=""/>
<mytags:postComments post="${model['post']}" start="0" max="8" outvar="comments"/>
<c:foreach items="comments" var="comment">
<!-- display a Comment in here -->
</c:forEach>
Pros:
- Controller stays simple, and does not contain presentation logic.
Cons:
- We have to create a taglib which pretty much exists just to allow the jsp to invoke a method on the domain object.
Exercise for the Reader. Which approach would you use, and why? Does it depend on the situation?