Is there any way to make some sort of parametrized macro on one JSP page and reuse it few times on the same page. JSP tags could be used but I would have to make one file per tag.
2 Answers
I've been wanting this functionality for years, and after googling yet again, I wrote my own. I think tag / jsp files & custom tag classes are great, but are often overkill for simple one-offs like you describe.
This is how my new "macro" tag now works (here used for simple html rendering of sortable table headers):
<%@ taglib prefix="tt" uri="/WEB-INF/tld/tags.tld" %>
<!-- define a macro to render a sortable header -->
<tt:macro id="sortable">
<th class="sortable">${headerName}
<span class="asc" >↑</span>
<span class="desc">↓</span>
</th>
</tt:macro>
<table><thead><tr>
<!-- use the macro for named headers -->
<tt:macro id="sortable" headerName="Name (this is sortable)" />
<tt:macro id="sortable" headerName="Age (this is sortable)" />
<th>Sex (not sortable)</th>
<!-- etc, etc -->
In /WEB-INF/tld/tags.tld, I added:
<tag>
<name>macro</name>
<tag-class>com.acme.web.taglib.MacroTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<description>ID of macro to call or define</description>
<name>id</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<dynamic-attributes>true</dynamic-attributes>
</tag>
And, finally, the Java tag class:
public class MacroTag
extends SimpleTagSupport implements DynamicAttributes
{
public static final String PREFIX = "MacroTag_";
private boolean bodyless = true;
private String id;
private Map<String, Object> attributes = new HashMap<String, Object>();
@Override public void setJspBody(JspFragment jspFragment) {
super.setJspBody(jspFragment);
getJspContext().setAttribute(PREFIX + id, jspFragment, PageContext.REQUEST_SCOPE);
bodyless = false;
}
@Override public void doTag() throws JspException, IOException {
if (bodyless) {
JspFragment jspFragment = (JspFragment) getJspContext().getAttribute(PREFIX + id, PageContext.REQUEST_SCOPE);
JspContext ctx = jspFragment.getJspContext();
for (String key : attributes.keySet())
ctx.setAttribute(key, attributes.get(key));
jspFragment.invoke(getJspContext().getOut());
for (String key : attributes.keySet()) {
ctx.removeAttribute(key);
}
}
}
public void setId(String id) {
this.id = id;
}
@Override public void setDynamicAttribute(String uri, String key, Object val) throws JspException {
attributes.put(key, val);
}
}
The implementation is pretty basic. If the tag has a body, we assume we're defining a macro, and we store that JspFragment. Otherwise, we assume we're calling a macro, so we look it up, and copy any dynamic attributes into its context so it be properly parameterized, and render it into the calling output stream.
Crazy this isn't built into JSP.

- 41
- 4
-
1That's very useful. – Aylwyn Lake May 27 '17 at 03:31
-
Added @Stefan 's bug fix (I'm an SO n00b and I don't think I yet have enough respect points to give respect points). And glad to hear AylwynLake ! – JohnnyMarnell Mar 13 '19 at 17:00
I tried the solution from Johnny and found, that if you use the macro multiple times, there is a bug.
you have to remove the attributes from the page context after renering
jspFragment.invoke(getJspContext().getOut());
for (String key : attributes.keySet()) {
ctx.removeAttribute(key);
}

- 69
- 2