One of the (many) reasons I switched from JSP to FreeMarker is that I couldn’t achieve what I describe in this post. Tutorials or blog posts regarding this situation were never to be found, and in addition it was really hard to find anyone considering this issue a real problem.
The problem
Suppose we are building an issue tracking system. We have a rich Domain Model which includes entities such as User
, Project
, Account
, Role
etc. We’ve also got an abstract Issue
object which is the root of the issue’s hierarchy. Concrete classes extending Issue
include Bug
, Feature
, Request
and Change
. These 4 POJOs inherit common fields from Issue
but add fields, methods and logic of their own.
Each of the issue’s subclass will need to have a slightly different HTML view. I tend to use the composite design pattern for my views, so I can break the HTML down to small reusable components. So it is obvious that we’re going to need 4 different views, one for each of them. Here are those issue rendering methods (presented in an imaginary pseudolanguage which combines EL, HTML and functions):
renderBug(bug) {
<fieldset>
<legend>Bug #${bug.id}</legend>
<p>Author: ${bug.author}</p>
<p>Date: ${bug.date}</p>
<p>Description: ${bug.description}</p>
<p>Steps to recreate bug: ${bug.stepsToRecreate}</p>
</fieldset>
}
renderFeature(feature) {
<fieldset>
<legend>feature #${feature.id}</legend>
<p>Author: ${feature.author}</p>
<p>Date: ${feature.date}</p>
<p>Description: ${feature.description}</p>
<p>Related URL: ${feature.url}</p>
<p>Screenshot upload: <img src="${feature.screenshot}" /></p>
</fieldset>
}
...
Our DAO (probably called IssueDao) is going to fetch a Collection<Issue>
(a bunch of issues) from the database for a particular use case. The runtime type of each of those entities cannot be Issue
but it will be Bug
, Feature
, Request
or Change
. The problem is that we are presenting them altogether in the same screen, so in order to render them we have to write code such as this:
foreach(issues as issue) {
if (issue instanceof Bug) renderBug(issue); continue;
if (issue instanceof Feature) renderFeature(issue); continue;
if (issue instanceof Request) renderRequest(issue); continue;
if (issue instanceof Change) renderChange(issue);
}
If this doesn’t seem very bad to you, here is an actual view implementation of a slightly bigger hierarchy using JSP 2.0 Tag Files:
if (t instanceof ActivityInternal) {%><p:activityInternalView pojo="${t}" /><%;}
if (t instanceof ActivityExternal) {%><p:activityExternalView pojo="${t}" /><%;}
if (t instanceof ActivityMilestone) {%><p:activityMilestoneView pojo="${t}" /><%;}
if (t instanceof Review) {%><p:reviewView pojo="${t}" /><%;}
if (t instanceof PublicationReport) {%><p:publicationReportView pojo="${t}" /><%;}
if (t instanceof PublicationWebsite) {%><p:publicationWebsiteView pojo="${t}" /><%;}
if (t instanceof InfoConference) {%><p:infoConferenceView pojo="${t}" /><%;}
if (t instanceof InfoBase) {%><p:infoBaseView pojo="${t}" /><%;}
if (t instanceof InfoChannel) {%><p:infoChannelView pojo="${t}" /><%;}
if (t instanceof Meeting) {%><p:meetingView pojo="${t}" /><%;}
if (t instanceof Interpretation) {%><p:interpretationView pojo="${t}" /><%;}
if (t instanceof BudgetItem) {%><p:budgetItemView pojo="${t}" /><%;}
if (t instanceof FocusGeneral) {%><p:focusGeneralView pojo="${t}" /><%;}
if (t instanceof FocusResearch) {%><p:focusResearchView pojo="${t}" /><%;}
if (t instanceof Risk) {%><p:riskView pojo="${t}" /><%;}
if (t instanceof QAChecklist) {%><p:QAChecklistView pojo="${t}" /><%;}
if (t instanceof TargetAudience) {%><p:targetAudienceView pojo="${t}" /><%;}
if (t instanceof LessonsLearned) {%><p:lessonsLearnedView pojo="${t}" /><%;}
If you still don’t think this is bad, you can stop reading ;)
What not to do
In a project I did in my early JSP days, what I did was to put all the view logic in the Java class! So it looked like this (this is actual Java):
public class Bug extends Issue {
...
public String renderMe() {
return "<fieldset><legend>" + this.getName() + "</legend>" +
"<p>Author: " + this.getAuthor() + "</p>" +
"<p>Date: " + this.getDate() + "</p>" +
"<p>Description: " + this.getDescription() + "</p>" +
"</fieldset>";
}
}
Although this type of code is a perfect candidate for The Daily WTF, the (only) advantage was that I could now render my pojos using (pseudocode):
foreach(issues as issue) {
issue.renderMe();
}
The solution
It seems that all we want is the ability to construct and dynamically (reflectively in Java terms) call the appropriate render tag each time. In freemarker we define macros which look like this:
<#macro renderBug bug>
<fieldset>
<legend>Bug #${bug.id}</legend>
<p>Author: ${bug.author}</p>
<p>Date: ${bug.date}</p>
<p>Description: ${bug.description}</p>
<p>Steps to recreate bug: ${bug.stepsToRecreate}</p>
</fieldset>
</#macro>
We need a way to call renderXXX where XXX is the short class name of the issue in question. And here is how you can do this in freemarker:
<#local macroname='render' + issue.class.name?split(".")?last />
<@.vars[macroname] issue />
For an issue of runtime type com.example.Foo, it concatenates the word “render” with “Foo” and calls the macro with that name. The magic happens with the help of the .vars special variable. It allows us to access variables by name. The full code now becomes:
<#macro renderIssue issue>
<#local macroname='render' + issue.class.name?split(".")?last />
<@.vars[macroname] issue />
</#macro>
<#list issues as issue>
<@renderIssue issue />
</#list>
By the way, this capability is usually present in dynamic scripting languages. So for example there are many ways to do that in PHP.
using dynamic evaluation
$functionName = "renderBug";
$functionName($issue);
eval("renderBug($issue);");
using call_user_func (probably safest of all)
call_user_func("renderBug", $issue);