There are plenty of times when you might want to alter the default views of the portal. I'm not going to go into the why since that is hugely subjective. What I do want to show is the how you can do this in the most maintainable way possible.
The largest concern with core JSP modification in projects is of course maintainability. JSPs have no "extensability" feature of their own and so when taking modified core JSPs into your project you run the risk of causing yourself a tone of heartache during future upgrades.
JSPs don't have any contract that allows for cleanly identifying when something has changed or gone wrong between versions. At least with pure source code you can take a leap and just try compiling against the latest version and although you likely run into many errors, they are quite literally spelled out for you. With JSPs you don't have this luxery and it's quite possible to go weeks or months with JSPs that seem to work after an upgrade but that suddenly start to exhibit bad behavior as your system starts to hit those edge cases or outright fail for the same reason. This is typically because of some unforseen change in the core logic.
Developers often try to mitigate this by doing things like using some complex or just "obvious" comments explaining changed code, hoping they are still around to review the changes on future upgrades...
What's likely to happen is that there ends up being so many JSPs changed and/or that the same developer is not around for the next upgrade, a complete and thorough review of those changes simply doesn't get done, risks are run and the ensuing issues become costly.
So goes the story of "JSP Hell"!
Meanwhile JSPs are still the best performing RAD view techology in java, so they are still used quite heavily.
What to do?
Liferay has long experienced this scenario and though we knew that changing from JSP was either unlikely or unreasonable considering the alternatives, we still had to come up with a good solution to the problem.
The solution is quite simple, the JSP Include Buffer.
The basic principle is to simply buffer, then extend the result that is output from the original JSP. Thus we can change only the parts we care about, and latter if there is a change in the original, it will either cause our extension to fail, or it will be seamlessly integrated, exactly the way a source code change would (even though it won't be a build time error, it will still be far more evident than the previous alternative).
There is one limitation, the JSP that you want to perform this operation on must NOT be a jsp fragment (a fragement is a JSP that you include using the low level <%@ include file="****.jsp" %> directive.
Rather it should be one using an include tag, such as <jsp:include /> or in the case of Liferay <liferay-util:include />, i.e. one that is invoked through a request dispatcher.
The examples I'm going to show will be using the <liferay-uitl:include /> tag.
From a JSP Hook
Liferay offers a plugin type called a "Hook" that allows you to define JSPs that should override (not overwrite) JSPs in the core, and on deploy these jsps replace those in the core. BUT the core JSPs are not lost (remember override not overwrite). The core JSPs are simply re-located, thus they are still accessible.
The core JSPs are relocated to a new name which injects the keyword .portal between the file name and extension. Thus a core JSP having the name start.jsp after being "Hooked" would be located under the name start.portal.jsp (at the exact same path).
How does this help us with the above problem?
Simply it means that we have the original to work with and so we don't need to completely re-implement or reproduce the complete code in our "extended" implementation. We can still call the original, buffer it's output and then manipulate the result to our needs.
Here is a very simple example or wrapping the output of the original with a simple black box (you could do this via CSS but what's the fun in that, and this example could serve many more uses that simple css styling can ever achieve).
<%@ taglib uri="http://liferay.com/tld/util" prefix="liferay-util" %> <liferay-util:buffer var="html"> <liferay-util:include page="/html/taglib/ui/page_iterator/start.portal.jsp" useCustomPage="<%= false %>" /> </liferay-util:buffer> <div style="border: 4px solid red; padding: 4px;"> <%= html %> </div>
From a FreeMarker Template
Can the same be achieved from Freemarker you ask? Certainly! And here is the same example as above.
<#assign liferay_util = PortalJspTagLibs["/WEB-INF/tld/liferay-util.tld"] /> <@liferay_util["buffer"] var="html"> <@liferay_util["include"] page="/html/taglib/ui/page_iterator/start.jsp" strict=true useCustomPage=false /> </@> <div style="border: 4px solid red; padding: 4px;"> ${html} </div>
From a Velocity Template
Hold up! You're kidding right? Velocity can't do jsp tags...
Well it can! It's just not pretty. Why would you do this from a Velocity anyway? Well, you never know what people want to do in their themes and the next blog post I write about how to override JSPs from theme templates will make this here feature all the more interesting for theme developers, AND by supporting Velocity, won't force all those developers to convert their existing themes to Freemarker just so they can override the odd JSP.
So, here goes (remember I don't promise that this is beautiful, only that it works and works well).
#set ($bufferTagClass = $portal.class.forName("com.liferay.taglib.util.BufferTag"))
#set ($includeTagClass = $portal.class.forName("com.liferay.taglib.util.IncludeTag"))
#set ($bufferTag = $bufferTagClass.newInstance())
#set ($V = $bufferTag.setPageContext($pageContext))
#set ($V = $bufferTag.setParent(null))
#set ($V = $bufferTag.setVar('html'))
#if ($bufferTag.doStartTag() == 2)
#set ($V = $bufferTag.setBodyContent($pageContext.pushBody()))
#set ($V = $bufferTag.doInitBody())
#set ($includeTag = $includeTagClass.newInstance())
#set ($V = $includeTag.setPageContext($pageContext))
#set ($V = $includeTag.setPage('/html/taglib/ui/page_iterator/start.jsp'))
#set ($V = $includeTag.setStrict(true))
#set ($V = $includeTag.setUseCustomPage(false))
#set ($V = $includeTag.runTag())
#set ($V = $bufferTag.doAfterBody())
#set ($V = $pageContext.popBody())
#set ($V = $bufferTag.doEndTag())
#end
<div style="border: 4px solid red; padding: 4px;">
${pageContext.findAttribute('html')}
</div>
And there you have it!
Now you'll notice that I bolded a coupled of lines above in the template examples. These lines, which set the field strict on the include tag to true, are VERY important. They tell the underlying tag NOT check if there are overrides for the specified JSP include (otherwise you may end up with infinite recursion and likely stack overflow errors ;) ).
I hope this can help some of you to biuld more maintainable solutions on Liferay with less fear of the upgrade process.


