At work I've been tasked with turning a bunch of HTML
files into a simple JSP
project. It's really all static, no serverside logic to program. I should mention I'm completely new to Java. JSP files seem to make it easy to work with common includes and variables, much like PHP
, but I'd like to know a simple way to get something like template inheritance (Django
style) or at least be able to have a base.jsp file containing the header and the footer, so I can insert content later.
Ben Lings seems to offer some hope in his answer here: JSP template inheritance Can someone explain how to achieve this?
Given that I don't have much time I think dynamic routing is a little much, so I'm happy to just to have URLs map directly onto .jsp
files, but I'm open to suggestion.
Thanks.
edit: I don't want to use any external libraries, because it would increase the learning curve for myself and others who work on the project, and the company I work for has been contracted to do this.
Another edit: I'm not sure if JSP tags
will be useful because my content doesn't really have any template variables. What I need is a way to be able to do this:
base.html:
<html><body>
{ content.body }
</body></html>
somepage.html
<wrapper:base.html>
<h1>Welcome</h1>
</wrapper>
with the output being:
<html><body>
<h1>Welcome</h1>
</body></html>
I think this would give me enough versatility to do everything I need. It could be achieved with includes
but then I would need a top and a bottom include for each wrapper, which is kind of messy.
This question is related to
java
jsp
inheritance
templates
tags
Based on the same basic idea as in @Will Hartung's answer, here is my magic one-tag extensible template engine. It even includes documentation and an example :-)
WEB-INF/tags/block.tag:
<%--
The block tag implements a basic but useful extensible template system.
A base template consists of a block tag without a 'template' attribute.
The template body is specified in a standard jsp:body tag, which can
contain EL, JSTL tags, nested block tags and other custom tags, but
cannot contain scriptlets (scriptlets are allowed in the template file,
but only outside of the body and attribute tags). Templates can be
full-page templates, or smaller blocks of markup included within a page.
The template is customizable by referencing named attributes within
the body (via EL). Attribute values can then be set either as attributes
of the block tag element itself (convenient for short values), or by
using nested jsp:attribute elements (better for entire blocks of markup).
Rendering a template block or extending it in a child template is then
just a matter of invoking the block tag with the 'template' attribute set
to the desired template name, and overriding template-specific attributes
as necessary to customize it.
Attribute values set when rendering a tag override those set in the template
definition, which override those set in its parent template definition, etc.
The attributes that are set in the base template are thus effectively used
as defaults. Attributes that are not set anywhere are treated as empty.
Internally, attributes are passed from child to parent via request-scope
attributes, which are removed when rendering is complete.
Here's a contrived example:
====== WEB-INF/tags/block.tag (the template engine tag)
<the file you're looking at right now>
====== WEB-INF/templates/base.jsp (base template)
<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="t" tagdir="/WEB-INF/tags" %>
<t:block>
<jsp:attribute name="title">Template Page</jsp:attribute>
<jsp:attribute name="style">
.footer { font-size: smaller; color: #aaa; }
.content { margin: 2em; color: #009; }
${moreStyle}
</jsp:attribute>
<jsp:attribute name="footer">
<div class="footer">
Powered by the block tag
</div>
</jsp:attribute>
<jsp:body>
<html>
<head>
<title>${title}</title>
<style>
${style}
</style>
</head>
<body>
<h1>${title}</h1>
<div class="content">
${content}
</div>
${footer}
</body>
</html>
</jsp:body>
</t:block>
====== WEB-INF/templates/history.jsp (child template)
<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="t" tagdir="/WEB-INF/tags" %>
<t:block template="base" title="History Lesson">
<jsp:attribute name="content" trim="false">
<p>${shooter} shot first!</p>
</jsp:attribute>
</t:block>
====== history-1977.jsp (a page using child template)
<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="t" tagdir="/WEB-INF/tags" %>
<t:block template="history" shooter="Han" />
====== history-1997.jsp (a page using child template)
<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="t" tagdir="/WEB-INF/tags" %>
<t:block template="history" title="Revised History Lesson">
<jsp:attribute name="moreStyle">.revised { font-style: italic; }</jsp:attribute>
<jsp:attribute name="shooter"><span class="revised">Greedo</span></jsp:attribute>
</t:block>
--%>
<%@ tag trimDirectiveWhitespaces="true" %>
<%@ tag import="java.util.HashSet, java.util.Map, java.util.Map.Entry" %>
<%@ tag dynamic-attributes="dynattributes" %>
<%@ attribute name="template" %>
<%
// get template name (adding default .jsp extension if it does not contain
// any '.', and /WEB-INF/templates/ prefix if it does not start with a '/')
String template = (String)jspContext.getAttribute("template");
if (template != null) {
if (!template.contains("."))
template += ".jsp";
if (!template.startsWith("/"))
template = "/WEB-INF/templates/" + template;
}
// copy dynamic attributes into request scope so they can be accessed from included template page
// (child is processed before parent template, so only set previously undefined attributes)
Map<String, String> dynattributes = (Map<String, String>)jspContext.getAttribute("dynattributes");
HashSet<String> addedAttributes = new HashSet<String>();
for (Map.Entry<String, String> e : dynattributes.entrySet()) {
if (jspContext.getAttribute(e.getKey(), PageContext.REQUEST_SCOPE) == null) {
jspContext.setAttribute(e.getKey(), e.getValue(), PageContext.REQUEST_SCOPE);
addedAttributes.add(e.getKey());
}
}
%>
<% if (template == null) { // this is the base template itself, so render it %>
<jsp:doBody/>
<% } else { // this is a page using the template, so include the template instead %>
<jsp:include page="<%= template %>" />
<% } %>
<%
// clean up the added attributes to prevent side effect outside the current tag
for (String key : addedAttributes) {
jspContext.removeAttribute(key, PageContext.REQUEST_SCOPE);
}
%>
Use tiles. It saved my life.
But if you can't, there's the include tag, making it similar to php.
The body tag might not actually do what you need it to, unless you have super simple content. The body tag is used to define the body of a specified element. Take a look at this example:
<jsp:element name="${content.headerName}"
xmlns:jsp="http://java.sun.com/JSP/Page">
<jsp:attribute name="lang">${content.lang}</jsp:attribute>
<jsp:body>${content.body}</jsp:body>
</jsp:element>
You specify the element name, any attributes that element might have ("lang" in this case), and then the text that goes in it--the body. So if
content.headerName = h1
,content.lang = fr
, andcontent.body = Heading in French
Then the output would be
<h1 lang="fr">Heading in French</h1>
Add dependecies for use <%@tag description="User Page template" pageEncoding="UTF-8"%>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>javax.servlet.jsp.jstl-api</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
</dependencies>
This can also be achieved with jsp:include. Chad Darby explains well here in this video https://www.youtube.com/watch?v=EWbYj0qoNHo
I know this answer is coming years after the fact and there is already a great JSP answer by Will Hartung, but there is Facelets, they are even mentioned in the answers from the linked question in the original question.
Facelets SO tag description
Facelets is an XML-based view technology for the JavaServer Faces framework. Designed specifically for JSF, Facelets is intended to be a simpler and more powerful alternative to JSP-based views. Initially a separate project, the technology was standardized as part of JSF 2.0 and Java-EE 6 and has deprecated JSP. Almost all JSF 2.0 targeted component libraries do not support JSP anymore, but only Facelets.
Sadly the best plain tutorial description I found was on Wikipedia and not a tutorial site. In fact the section describing templates even does along the lines of what the original question was asking for.
Due to the fact that Java-EE 6 has deprecated JSP I would recommend going with Facelets despite the fact that it looks like there might be more required for little to no gain over JSP.
I made quite easy, Django style JSP Template inheritance tag library. https://github.com/kwon37xi/jsp-template-inheritance
I think it make easy to manage layouts without learning curve.
example code :
base.jsp : layout
<%@page contentType="text/html; charset=UTF-8" %>
<%@ taglib uri="http://kwonnam.pe.kr/jsp/template-inheritance" prefix="layout"%>
<!DOCTYPE html>
<html lang="en">
<head>
<title>JSP Template Inheritance</title>
</head>
<h1>Head</h1>
<div>
<layout:block name="header">
header
</layout:block>
</div>
<h1>Contents</h1>
<div>
<p>
<layout:block name="contents">
<h2>Contents will be placed under this h2</h2>
</layout:block>
</p>
</div>
<div class="footer">
<hr />
<a href="https://github.com/kwon37xi/jsp-template-inheritance">jsp template inheritance example</a>
</div>
</html>
view.jsp : contents
<%@page contentType="text/html; charset=UTF-8" %>
<%@ taglib uri="http://kwonnam.pe.kr/jsp/template-inheritance" prefix="layout"%>
<layout:extends name="base.jsp">
<layout:put name="header" type="REPLACE">
<h2>This is an example about layout management with JSP Template Inheritance</h2>
</layout:put>
<layout:put name="contents">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin porta,
augue ut ornare sagittis, diam libero facilisis augue, quis accumsan enim velit a mauris.
</layout:put>
</layout:extends>
Source: Stackoverflow.com