Java tutorial
/* * Weblounge: Web Content Management System * Copyright (c) 2003 - 2011 The Weblounge Team * http://entwinemedia.com/weblounge * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package ch.entwine.weblounge.common.impl.content.page; import static ch.entwine.weblounge.common.site.Environment.Any; import ch.entwine.weblounge.common.content.RenderException; import ch.entwine.weblounge.common.content.page.HTMLHeadElement; import ch.entwine.weblounge.common.content.page.Link; import ch.entwine.weblounge.common.content.page.PageTemplate; import ch.entwine.weblounge.common.content.page.Script; import ch.entwine.weblounge.common.impl.site.SiteImpl; import ch.entwine.weblounge.common.impl.util.config.ConfigurationUtils; import ch.entwine.weblounge.common.impl.util.xml.XPathHelper; import ch.entwine.weblounge.common.request.CacheTag; import ch.entwine.weblounge.common.request.RequestFlavor; import ch.entwine.weblounge.common.request.WebloungeRequest; import ch.entwine.weblounge.common.request.WebloungeResponse; import ch.entwine.weblounge.common.site.Environment; import ch.entwine.weblounge.common.site.Site; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import java.net.MalformedURLException; import java.net.URL; import java.util.Iterator; import java.util.Map; import javax.xml.namespace.NamespaceContext; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathFactory; /** * This renderer implements a page template that is backed by a Java Server * Page. */ public class PageTemplateImpl extends AbstractRenderer implements PageTemplate { /** The logging facility */ private final Logger logger = LoggerFactory.getLogger(PageTemplateImpl.class); /** Default composer for action output */ protected String stage = DEFAULT_STAGE; /** Default page layout */ protected String layout = null; /** Is this the default template? */ protected boolean isDefault = false; /** The site */ protected Site site = null; /** * Creates a new page template. */ public PageTemplateImpl() { addFlavor(RequestFlavor.HTML); } /** * Creates a new page template that is backed by a Java Server Page located at * <code>url</code>. * * @param identifier * the template identifier * @param url * the renderer url */ public PageTemplateImpl(String identifier, URL url) { super(identifier, url); addFlavor(RequestFlavor.HTML); } /** * {@inheritDoc} * * @see ch.entwine.weblounge.common.content.page.PageTemplate#setSite(ch.entwine.weblounge.common.site.Site) */ public void setSite(Site site) { if (site == null) throw new IllegalArgumentException("Site must not be null"); this.site = site; for (HTMLHeadElement htmlHead : headers) { htmlHead.setSite(site); } if (!Any.equals(environment)) { processURLTemplates(environment); } } /** * {@inheritDoc} * * @see ch.entwine.weblounge.common.content.page.PageTemplate#setStage(java.lang.String) */ public void setStage(String stage) { this.stage = stage; } /** * {@inheritDoc} * * @see ch.entwine.weblounge.common.content.page.PageTemplate#getStage() */ public String getStage() { return stage; } /** * {@inheritDoc} * * @see ch.entwine.weblounge.common.content.page.PageTemplate#getDefaultLayout() */ public String getDefaultLayout() { return layout; } /** * {@inheritDoc} * * @see ch.entwine.weblounge.common.content.page.PageTemplate#setDefaultLayout(java.lang.String) */ public void setDefaultLayout(String layout) { this.layout = layout; } /** * {@inheritDoc} * * @see ch.entwine.weblounge.common.content.page.PageTemplate#setDefault(boolean) */ public void setDefault(boolean v) { isDefault = v; } /** * {@inheritDoc} * * @see ch.entwine.weblounge.common.content.page.PageTemplate#isDefault() */ public boolean isDefault() { return isDefault; } /** * {@inheritDoc} * * @see ch.entwine.weblounge.common.content.page.PageTemplate#render(ch.entwine.weblounge.common.request.WebloungeRequest, * ch.entwine.weblounge.common.request.WebloungeResponse) */ public void render(WebloungeRequest request, WebloungeResponse response) throws RenderException { // Adjust revalidation and expiration time response.setClientRevalidationTime(getClientRevalidationTime()); response.setCacheExpirationTime(getCacheExpirationTime()); // Add cache support response.addTag(CacheTag.Renderer, getIdentifier()); URL renderer = renderers.get(RendererType.Page.toString().toLowerCase()); includeJSP(request, response, renderer); } /** * {@inheritDoc} * * @see ch.entwine.weblounge.common.impl.content.GeneralComposeable#hashCode() */ @Override public int hashCode() { return super.hashCode(); } /** * {@inheritDoc} * * @see ch.entwine.weblounge.common.impl.content.GeneralComposeable#equals(java.lang.Object) */ @Override public boolean equals(Object o) { // This is to indicate that using the super implementation is sufficient return super.equals(o); } /** * Initializes this page template from an XML node that was generated using * {@link #toXml()}. * <p> * To speed things up, you might consider using the second signature that uses * an existing <code>XPath</code> instance instead of creating a new one. * * @param node * the page template node * @throws IllegalStateException * if the page template cannot be parsed * @see #fromXml(Node, XPath) * @see #toXml() */ public static PageTemplate fromXml(Node node) throws IllegalStateException { XPath xpath = XPathFactory.newInstance().newXPath(); // Define the xml namespace xpath.setNamespaceContext(new NamespaceContext() { public String getNamespaceURI(String prefix) { return "ns".equals(prefix) ? SiteImpl.SITE_XMLNS : null; } public String getPrefix(String namespaceURI) { return null; } public Iterator<?> getPrefixes(String namespaceURI) { return null; } }); return fromXml(node, xpath); } /** * Initializes this page template from an XML node that was generated using * {@link #toXml()}. * * @param node * the page template node * @param xpath * the xpath processor * @throws IllegalStateException * if the page template cannot be parsed * @see #fromXml(Node) * @see #toXml() */ public static PageTemplate fromXml(Node node, XPath xpath) throws IllegalStateException { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Identifier String id = XPathHelper.valueOf(node, "@id", xpath); if (id == null) throw new IllegalStateException("Missing id in page template definition"); // Class String className = XPathHelper.valueOf(node, "class", xpath); // Renderer url URL rendererUrl = null; String rendererUrlNode = XPathHelper.valueOf(node, "ns:renderer", xpath); if (rendererUrlNode == null) throw new IllegalStateException("Missing renderer in page template definition"); try { rendererUrl = new URL(rendererUrlNode); } catch (MalformedURLException e) { throw new IllegalStateException( "Malformed renderer url in page template definition: " + rendererUrlNode); } // Create the page template PageTemplate template = null; if (className != null) { Class<? extends PageTemplate> c = null; try { c = (Class<? extends PageTemplate>) classLoader.loadClass(className); template = c.newInstance(); template.setIdentifier(id); template.setRenderer(rendererUrl); } catch (ClassNotFoundException e) { throw new IllegalStateException( "Implementation " + className + " for page template '" + id + "' not found", e); } catch (InstantiationException e) { throw new IllegalStateException( "Error instantiating impelementation " + className + " for page template '" + id + "'", e); } catch (IllegalAccessException e) { throw new IllegalStateException("Access violation instantiating implementation " + className + " for page template '" + id + "'", e); } catch (Throwable t) { throw new IllegalStateException( "Error loading implementation " + className + " for page template '" + id + "'", t); } } else { template = new PageTemplateImpl(id, rendererUrl); } // Composeable template.setComposeable(ConfigurationUtils.isTrue(XPathHelper.valueOf(node, "@composeable", xpath))); // Default template.setDefault(ConfigurationUtils.isTrue(XPathHelper.valueOf(node, "@default", xpath))); // Stage String stage = XPathHelper.valueOf(node, "ns:stage", xpath); if (stage != null) template.setStage(stage); // Layout String layout = XPathHelper.valueOf(node, "ns:layout", xpath); if (layout != null) template.setDefaultLayout(layout); // client revalidation time String recheck = XPathHelper.valueOf(node, "ns:recheck", xpath); if (recheck != null) { try { template.setClientRevalidationTime(ConfigurationUtils.parseDuration(recheck)); } catch (NumberFormatException e) { throw new IllegalStateException( "The page template revalidation time is malformed: '" + recheck + "'"); } catch (IllegalArgumentException e) { throw new IllegalStateException( "The page template revalidation time is malformed: '" + recheck + "'"); } } // cache expiration time String valid = XPathHelper.valueOf(node, "ns:valid", xpath); if (valid != null) { try { template.setCacheExpirationTime(ConfigurationUtils.parseDuration(valid)); } catch (NumberFormatException e) { throw new IllegalStateException("The page template valid time is malformed: '" + valid + "'", e); } catch (IllegalArgumentException e) { throw new IllegalStateException("The page template valid time is malformed: '" + valid + "'", e); } } // name String name = XPathHelper.valueOf(node, "m:name", xpath); template.setName(name); // scripts NodeList scripts = XPathHelper.selectList(node, "ns:includes/ns:script", xpath); for (int i = 0; i < scripts.getLength(); i++) { template.addHTMLHeader(ScriptImpl.fromXml(scripts.item(i))); } // links NodeList links = XPathHelper.selectList(node, "ns:includes/ns:link", xpath); for (int i = 0; i < links.getLength(); i++) { template.addHTMLHeader(LinkImpl.fromXml(links.item(i))); } return template; } /** * Returns an XML representation of this renderer. * * @return the xml representation */ public String toXml() { StringBuffer buf = new StringBuffer(); buf.append("<template"); buf.append(" id=\"").append(identifier).append("\""); buf.append(" composeable=\"").append(composeable).append("\""); if (isDefault) buf.append(" default=\"true\""); buf.append(">"); // Names if (StringUtils.isNotBlank(name)) { buf.append("<name><![CDATA["); buf.append(name); buf.append("]]></name>"); } // Renderer class if (!this.getClass().equals(PageTemplateImpl.class)) buf.append("<class>").append(getClass().getName()).append("</class>"); // Renderer url for (Map.Entry<String, URL> entry : renderers.entrySet()) { if (renderers.size() > 1) buf.append("<renderer type=\"").append(entry.getKey()).append("\">"); else buf.append("<renderer>"); buf.append(entry.getValue().toExternalForm()).append("</renderer>"); } // Stage name if (stage != null && !DEFAULT_STAGE.equals(stage)) buf.append("<stage>").append(stage).append("</stage>"); // Default page layout if (layout != null) buf.append("<layout>").append(layout).append("</layout>"); // Recheck time if (clientRevalidationTime >= 0) { buf.append("<recheck>"); buf.append(ConfigurationUtils.toDuration(clientRevalidationTime)); buf.append("</recheck>"); } // Valid time if (cacheExpirationTime >= 0) { buf.append("<valid>"); buf.append(ConfigurationUtils.toDuration(cacheExpirationTime)); buf.append("</valid>"); } // Includes if (getHTMLHeaders().length > 0) { buf.append("<includes>"); for (HTMLHeadElement header : getHTMLHeaders()) { if (header instanceof Link) buf.append(header.toXml()); } for (HTMLHeadElement header : getHTMLHeaders()) { if (header instanceof Script) buf.append(header.toXml()); } buf.append("</includes>"); } buf.append("</template>"); return buf.toString(); } /** * Processes both renderer and editor url by replacing templates in their * paths with real values from the actual module. * * @param environment * the environment * * @return <code>false</code> if the paths don't end up being real urls, * <code>true</code> otherwise */ private boolean processURLTemplates(Environment environment) { if (site == null) throw new IllegalStateException("Site cannot be null"); // Process the renderer URL for (Map.Entry<String, URL> entry : renderers.entrySet()) { URL renderer = entry.getValue(); String rendererURL = ConfigurationUtils.processTemplate(renderer.toExternalForm(), site, environment); try { renderer = new URL(rendererURL); renderers.put(entry.getKey(), renderer); } catch (MalformedURLException e) { logger.error("Renderer url {} of pagelet {} is malformed", rendererURL, this); } } // Process the head elements (scripts and stylesheet includes) for (HTMLHeadElement headElement : headers) { headElement.setEnvironment(environment); } return true; } /** * {@inheritDoc} * * @see ch.entwine.weblounge.common.impl.content.GeneralComposeable#setEnvironment(ch.entwine.weblounge.common.site.Environment) */ @Override public void setEnvironment(Environment environment) { if (environment == null) throw new IllegalArgumentException("Environment cannot be null"); // Is there anything we need to be doing? if (!environment.equals(this.environment) && site != null) { logger.debug("Processing url templates of {} with environment {}", this, environment); processURLTemplates(environment); } super.setEnvironment(environment); } }