In a previous blog I mentioned the technique of server side includes (SSIs). I promised to get back on that subject, well here we go!
If you create presentation WCBs, you will encounter caching. In general, this does not present any problems. However, when you want to display information that refreshes faster than the page itself is edited, regular caching will not work.
Let's say you want to build a pagepart that displays an indication of the time of day - like "Thursday morning, January 29th 2009". Let's assume the page is edited once per week. When the page is saved, its timestamp will be set and the cache will refresh. So if we don't take special precautions, the page will display "Thursday morning, January 29th, 2009" all week until the next edit. Argh!
Server side include to the rescue!
Way, way back in the days, when dinosaurs walked the earth and used NCSA Mosaic to browse the Web (technically a bit later, but I couldn't resist mentioning NCSA Mosaic ;-)), the httpd server offered an option to put a special fragment in your HTML to execute CGI scripts inline. For example:
<html>
<head>
<title>test</title>
</head>
<body>
Today is
<!--#include virtual="/cgi-bin/showdate.pl" -->
</body>
</html>
This fragment was named "server side include" because it prompts the server to include the output of a command on the side of the server (i.e. before sending a reponse to the browser). This option still exists today in the Apache HTTP Server even though scripting languages like JSP, ASP and PHP provide much more flexibility.
To provide more flexible ways of caching, GX WebManager builds on the same idea using server side include tags to include bits of separately generated content.
To make this more insightful, let us take a closer look at a server side include in action. For now, just assume that we have prepared our page part properly. We will examine the code to generate the server side include later. As you may recall, the page will be generated and cached.
<html>
<head>
<title>test</title>
</head>
<body>
<!--#include virtual="/web/show/some/include/url" -->
...
</body>
</html>
Before sending out the page, GX WebManager scans the source for server side includes. When the source contains an SSI tag, the URL is requested as if it was a normal request (meaning: it might be cached). The SSI tag is then replaced by the result of the request.
Let us examine how that works in our example. GX WebManager scans the source and encounters the SSI-tag. It requests the URL "/web/show/some/include/url", which results in HTML being generated something like this:
<div> Thursday morning, January 29th, 2009 </div>
And indeed, this result is also cached and also scanned for possible SSI-tags.
The result is then used to replace the SSI-tag in the generated page:
<html>
<head>
<title>test</title>
</head>
<body>
<div>
Thursday morning, January 29th, 2009
</div>
...
</body>
</html>
We have to somehow coerce the pagepart to do the right thing at the right moment. This can be achieved by adding the ssi-tag to the XML in the descriptor file for our pagepart ("helloworldDatepart.xml"):
<!-- helloworldDatepart.xml -->
<presentation>
<name>helloworld datepart</name>
<display-name>Helloworld datepart</display-name>
<scope>PagePart</scope>
<ssi>
<presentation>helloworldDatepartSsi</presentation>
</ssi>
</presentation>
<%-- helloworldDatepartSsi.jspf --%>
<%@ page language="java" session="false" buffer="none" %>
<%@ taglib uri="http://www.gx.nl/taglib/wm" prefix="wm" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<wm:link cto="900" passOn="cto"
ssiObjectId="${presentationcontext.pagePart.id}"
ssiObjectClassName="nl.gx.webmanager.cms.layout.PagePart" />
Note: The Caching document briefly touches on the subject of how caching reacts to URLs. To learn the gritty details you are better off scouring the source of the standard presentation for "*Ssi.jspf" files with working examples.
All that misses now is the normal content of our pagepart:<%@ page language="java" session="false" buffer="none" %>
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<c:set var="pagePart" value="${presentationcontext.pagePart}" />
<c:set var="blocks" value="${pagePart.blocks}" />
<c:set var="now" value="<%=new java.util.Date()%>" />
<fmt:setLocale value="en_US" />
<fmt:formatDate var="dayStr" pattern="EEEE" value="${now}" />
<fmt:formatDate var="monthStr" pattern="MMMM" value="${now}" />
<fmt:formatDate var="monthDayStr" pattern="d" value="${now}" />
<fmt:formatDate var="yearStr" pattern="yyyy" value="${now}" />
<fmt:formatDate var="hourStr" pattern="H" value="${now}" />
<c:choose>
<c:when test="${hourStr < 6}">
<c:set var="daypartStr" value="night" />
</c:when>
<c:when test="${hourStr >= 6 && hourStr < 12}">
<c:set var="daypartStr" value="morning" />
</c:when>
<c:when test="${hourStr >= 12 && hourStr < 18}">
<c:set var="daypartStr" value="afternoon" />
</c:when>
<c:when test="${hourStr >= 18}">
<c:set var="daypartStr" value="evening" />
</c:when>
</c:choose>
<c:choose>
<c:when test="${monthDayStr == 1 || monthDayStr == 21 || monthDayStr == 31}">
<c:set var="monthDayStr" value="${monthDayStr}st" />
</c:when>
<c:when test="${monthDayStr == 2 || monthDayStr == 22}">
<c:set var="monthDayStr" value="${monthDayStr}nd" />
</c:when>
<c:when test="${monthDayStr == 3 || monthDayStr == 23}">
<c:set var="monthDayStr" value="${monthDayStr}rd" />
</c:when>
<c:otherwise>
<c:set var="monthDayStr" value="${monthDayStr}th" />
</c:otherwise>
</c:choose>
${dayStr} ${daypartStr}, ${monthStr} ${monthDayStr}, ${yearStr}
<wm:pagePart label="helloworld datepart" />
When you examine your page with SSIdebug, you will notice a server side include with a lengthy URL in the spot where you put your pagepart:
<!--#include virtual="/web/show?id=26111&langid=42&cto=900&elementHolder=70972&ssiObjectClassName=nl.gx.webmanager.cms.layout.PagePart&ssiObjectId=76231" -->
Every unique URL will be cached. Take this into account when constructing your SSI link. Do not pass arguments that are not needed. Try and simplify the information in the link as much as possible. For example, if our SSI above would contain an extra "pageid=xxx" it would lead to a new URL for each and every page. However, our pagepart has exactly the same outcome for all pages! It is better to have that one request in the cache instead of separate requests for every page.
You can expose the SSIs on a page with SSIdebug. All SSIs on the page will be visible on the page. You can also take a look at the Administrator Status Tool to see which URLs are being requested. SSIs are regular requests, so you can see them flash by.
Did you log in as tomcat administrator? Are you requesting the URL on the backend server? If the URL does not contain "&frontendrequest=true", try adding it.
SSIs won't work for dumped pages - unless you configure Apache recognize them. Typically you dump the pages to ".shtml" and have the system administrator configure the Apache server with these options:
Options +Includes AddType text/html .shtml AddOutputFilter INCLUDES .shtml
As you can see there is a lot to know about server side includes. I hope this posting will help you on your way!
To sign off, some tales from the hard side:
Till next time,
Patrick
Patrick Atoon has gained nuff respect as one of the most experienced web architects in the GX Webmanager community or even the global hip hop community for that matter.
Read all Patricks blog entries
Other blog entries: