Normally with Tridion, we do navigation as a transformation between a navigation XML with an XSLT stylesheet. We plug some parameters into this transformation in order to highlight the current context.
In CWA (Content Web Application) terms, this approach still applies only with some modification. Namely, the XML and XSLT are usually both published to the CD (Content Delivery) database. This setup implies that we need a special way or reading the XML/XSLT, prior to performing the actual transformation. To this respect, I created a custom Java tag CwaTransformXml that first ensures the XML and XSLT files are extracted from the CD DB and serialized as files on the file system, then second, it performs the XSLT transformation.
CwaTransformXml has the following attributes:
Moreover, the tag caches the XML DOM document and the XSLT Template into the application scope, such that they don't need be parsed and recreated with every tag call. Also the output of the transformation can be cached in the PageContext, such that it can be reused on the same page.
Where CacheEntry class is just a POJO holding the following members:
In CWA (Content Web Application) terms, this approach still applies only with some modification. Namely, the XML and XSLT are usually both published to the CD (Content Delivery) database. This setup implies that we need a special way or reading the XML/XSLT, prior to performing the actual transformation. To this respect, I created a custom Java tag CwaTransformXml that first ensures the XML and XSLT files are extracted from the CD DB and serialized as files on the file system, then second, it performs the XSLT transformation.
CwaTransformXml has the following attributes:
- xml - the XML file path;
- xslt - the XSLT file path;
- paramName - the name of the parameter to pass into the transformation;
- paramValue - the value of the parameter;
- out - the variable name to set in PageContext holding the result of the transformation;
Moreover, the tag caches the XML DOM document and the XSLT Template into the application scope, such that they don't need be parsed and recreated with every tag call. Also the output of the transformation can be cached in the PageContext, such that it can be reused on the same page.
<t:CwaTransformXml xml="/en/system/nav.xml" xslt="/en/system/main.xslt"
paramName="CurrentPage" paramValue="tcm:2-3-64"/>
paramName="CurrentPage" paramValue="tcm:2-3-64"/>
The main tag logic is the following:
public int doStartTag() throws JspException {
try {
Document
xmlDom = (Document) CacheUtils.getFromCache(xml, pageContext, cache);
DOMSource
domSource = new DOMSource(xmlDom);
Templates
templates = (Templates) CacheUtils.getFromCache(xslt, pageContext, cache);
Transformer
transformer = templates.newTransformer();
setParams(transformer);
if (out == null) {
transformer.transform(domSource,
new StreamResult(pageContext.getOut()));
}
else {
StringWriter
writer = new StringWriter();
transformer.transform(domSource,
new
StreamResult(writer));
pageContext.setAttribute(out,
writer.toString());
}
}
catch (Exception e) {
throw new
JspException(e);
}
return SKIP_BODY;
}
The method CacheUtils.getFromCache() makes sure the path passed to it is retrieved from the CD DB and serialized to the file system (it is using CWA's class PageFileDistributionController), parses the XML DOM (or XSLT Templates object), places it into the application scope cache, then retrieves the actual DOM/Templates object.
public static Object
getFromCache(String name, PageContext pageContext, long cache) {
CacheEntry
cacheEntry = (CacheEntry) pageContext.getAttribute(name,
PageContext.APPLICATION_SCOPE);
PageContext.APPLICATION_SCOPE);
if (cacheEntry == null || System.currentTimeMillis()
> cacheEntry.getTtl()) {
cacheEntry
= setInCache(name, cacheEntry, pageContext, cache);
}
return cacheEntry.getItem();
}
private static synchronized CacheEntry
setInCache(String name, CacheEntry cacheEntry,
PageContext pageContext, long cache) {
PageContext pageContext, long cache) {
long currentTimeMillis
= System.currentTimeMillis();
if (cacheEntry == null) {
cacheEntry
= new CacheEntry(0, null);
pageContext.setAttribute(name,
cacheEntry, PageContext.APPLICATION_SCOPE);
}
if
(currentTimeMillis > cacheEntry.getTtl()) {
PageFileDistributionController
controller =
PageFileDistributionController.getInstance();
PageFileDistributionController.getInstance();
ServletContext
servletContext = pageContext.getServletContext();
HttpServletRequest
request = (HttpServletRequest) pageContext.getRequest();
File
file = controller.processPage(servletContext, request, name);
String
nameLower = name.toLowerCase();
Object
item = null;
if
(nameLower.endsWith(".xslt") || nameLower.endsWith(".xsl")) {
Source
source = new StreamSource(file);
item
= transformerFactory.newTemplates(source);
}
else if (name.endsWith(".xml")) {
DocumentBuilder
documentBuilder = documentBuilderFactory.newDocumentBuilder();
item
= documentBuilder.parse(file);
}
cacheEntry.setTtl(currentTimeMillis
+ cache);
cacheEntry.setItem(item);
}
return cacheEntry;
}
Where CacheEntry class is just a POJO holding the following members:
private long ttl;
private Object item;
Comments