ch.entwine.weblounge.common.impl.content.page.PageletRendererImpl.java Source code

Java tutorial

Introduction

Here is the source code for ch.entwine.weblounge.common.impl.content.page.PageletRendererImpl.java

Source

/*
 *  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.PagePreviewMode;
import ch.entwine.weblounge.common.content.page.PageletRenderer;
import ch.entwine.weblounge.common.content.page.Script;
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.Module;
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.Map;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathFactory;

/**
 * This renderer implements a pagelet renderer that is backed by a Java Server
 * Page.
 */
public class PageletRendererImpl extends AbstractRenderer implements PageletRenderer {

    /** The logging facility */
    private final Logger logger = LoggerFactory.getLogger(PageletRendererImpl.class);

    /** The editor url */
    protected URL editor = null;

    /** The defining module */
    protected Module module = null;

    /** The site */
    protected Site site = null;

    /** The preview mode */
    protected PagePreviewMode previewMode = PagePreviewMode.None;

    /**
     * Creates a new page template.
     */
    public PageletRendererImpl() {
        addFlavor(RequestFlavor.HTML);
    }

    /**
     * Creates a new page template with the given identifier.
     * 
     * @param identifier
     *          the template identifier
     */
    public PageletRendererImpl(String identifier) {
        this(identifier, null);
    }

    /**
     * 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 PageletRendererImpl(String identifier, URL url) {
        super(identifier, url);
        addFlavor(RequestFlavor.HTML);
    }

    /**
     * {@inheritDoc}
     * 
     * @see ch.entwine.weblounge.common.content.page.PageletRenderer#setModule(ch.entwine.weblounge.common.site.Module)
     */
    public void setModule(Module module) {
        if (module == null)
            throw new IllegalArgumentException("Module must not be null");
        this.module = module;
        this.site = module.getSite();
        for (HTMLHeadElement htmlHead : headers) {
            htmlHead.setSite(site);
            htmlHead.setModule(module);
        }
        if (!Any.equals(environment)) {
            processURLTemplates(environment);
        }
    }

    /**
     * {@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 must not be null");

        // Is there anything we need to be doing?
        if (!environment.equals(this.environment) && module != null) {
            logger.debug("Processing url templates of {} with environment {}", this, environment);
            processURLTemplates(environment);
        }

        super.setEnvironment(environment);
    }

    /**
     * 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 (module == null)
            throw new IllegalStateException("Module is null");
        if (module.getSite() == null)
            throw new IllegalArgumentException("Site is null");

        // Process the renderer URL
        for (Map.Entry<String, URL> entry : renderers.entrySet()) {
            URL renderer = entry.getValue();
            String rendererURL = ConfigurationUtils.processTemplate(renderer.toExternalForm(), module, environment);
            try {
                renderer = new URL(rendererURL);
                renderers.put(entry.getKey(), renderer);
            } catch (MalformedURLException e) {
                logger.warn("Renderer url {} of pagelet {} is malformed", rendererURL, this);
                return false;
            }
        }

        // Process the editor URL
        if (editor != null) {
            String editorURL = ConfigurationUtils.processTemplate(editor.toExternalForm(), module, environment);
            try {
                editor = new URL(editorURL);
            } catch (MalformedURLException e) {
                logger.warn("Editor url {} of pagelet {} is malformed", editorURL, this);
                return false;
            }
        }

        // 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#addHTMLHeader(ch.entwine.weblounge.common.content.page.HTMLHeadElement)
     */
    @Override
    public void addHTMLHeader(HTMLHeadElement header) {
        if (module != null) {
            if (module != null)
                header.setModule(module);
            if (site != null)
                header.setSite(site);
            if (!Any.equals(environment))
                header.setEnvironment(environment);
        }
        super.addHTMLHeader(header);
    }

    /**
     * {@inheritDoc}
     * 
     * @see ch.entwine.weblounge.common.content.page.PageletRenderer#getModule()
     */
    public Module getModule() {
        return module;
    }

    /**
     * {@inheritDoc}
     * 
     * @see ch.entwine.weblounge.common.content.page.PageletRenderer#setPreviewMode(ch.entwine.weblounge.common.content.Pagelet.PagePreviewMode)
     */
    public void setPreviewMode(PagePreviewMode mode) {
        this.previewMode = mode;
    }

    /**
     * {@inheritDoc}
     * 
     * @see ch.entwine.weblounge.common.content.page.PageletRenderer#getPreviewMode()
     */
    public PagePreviewMode getPreviewMode() {
        return previewMode;
    }

    /**
     * {@inheritDoc}
     * 
     * @see ch.entwine.weblounge.common.impl.content.page.AbstractRenderer#setRenderer(java.net.URL)
     */
    @Override
    public void setRenderer(URL renderer) {
        super.setRenderer(renderer);
        if (!Any.equals(environment) && module != null)
            processURLTemplates(environment);
    }

    /**
     * {@inheritDoc}
     * 
     * @see ch.entwine.weblounge.common.impl.content.page.AbstractRenderer#getRenderer()
     */
    @Override
    public URL getRenderer() {
        return getRenderer(RendererType.Page.toString());
    }

    /**
     * {@inheritDoc}
     * 
     * @see ch.entwine.weblounge.common.impl.content.page.AbstractRenderer#getRenderer(java.lang.String)
     */
    @Override
    public URL getRenderer(String type) {
        return super.getRenderer(type);
    }

    /**
     * {@inheritDoc}
     * 
     * @see ch.entwine.weblounge.common.content.page.PageletRenderer#setEditor(java.net.URL)
     */
    public void setEditor(URL editor) {
        this.editor = editor;
        if (!Any.equals(environment) && module != null)
            processURLTemplates(environment);
    }

    /**
     * {@inheritDoc}
     * 
     * @see ch.entwine.weblounge.common.content.page.PageletRenderer#getEditor()
     */
    public URL getEditor() {
        return editor;
    }

    /**
     * {@inheritDoc}
     * 
     * @see ch.entwine.weblounge.common.content.Renderer#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.Module, getModule().getIdentifier());
        response.addTag(CacheTag.Renderer, getIdentifier());

        URL renderer = renderers.get(RendererType.Page.toString().toLowerCase());
        includeJSP(request, response, renderer);
    }

    /**
     * {@inheritDoc}
     * 
     * @see ch.entwine.weblounge.common.content.page.PageletRenderer#renderAsEditor(ch.entwine.weblounge.common.request.WebloungeRequest,
     *      ch.entwine.weblounge.common.request.WebloungeResponse)
     */
    public void renderAsEditor(WebloungeRequest request, WebloungeResponse response) throws RenderException {
        includeJSP(request, response, editor);
    }

    /**
     * {@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) {
        if (!(o instanceof PageletRenderer))
            return false;
        PageletRenderer r = (PageletRenderer) o;
        if (module != null && !module.equals(r.getModule()))
            return false;
        return super.equals(o);
    }

    /**
     * Initializes this pagelet renderer 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 pagelet renderer node
     * @throws IllegalStateException
     *           if the pagelet renderer cannot be parsed
     * @see #fromXml(Node, XPath)
     * @see #toXml()
     */
    public static PageletRenderer fromXml(Node node) throws IllegalStateException {
        XPath xpath = XPathFactory.newInstance().newXPath();
        return fromXml(node, xpath);
    }

    /**
     * Initializes this pagelet renderer from an XML node that was generated using
     * {@link #toXml()}.
     * 
     * @param node
     *          the pagelet renderer node
     * @param xpath
     *          the xpath processor
     * @throws IllegalStateException
     *           if the pagelet renderer cannot be parsed
     * @see #fromXml(Node)
     * @see #toXml()
     */
    public static PageletRenderer 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, "m:class", xpath);

        // Create the pagelet renderer
        PageletRenderer renderer = null;
        if (className != null) {
            Class<? extends PageletRenderer> c = null;
            try {
                c = (Class<? extends PageletRenderer>) classLoader.loadClass(className);
                renderer = c.newInstance();
                renderer.setIdentifier(id);
            } catch (ClassNotFoundException e) {
                throw new IllegalStateException(
                        "Implementation " + className + " for pagelet renderer '" + id + "' not found", e);
            } catch (InstantiationException e) {
                throw new IllegalStateException(
                        "Error instantiating impelementation " + className + " for pagelet renderer '" + id + "'",
                        e);
            } catch (IllegalAccessException e) {
                throw new IllegalStateException("Access violation instantiating implementation " + className
                        + " for pagelet renderer '" + id + "'", e);
            } catch (Throwable t) {
                throw new IllegalStateException(
                        "Error loading implementation " + className + " for pagelet renderer '" + id + "'", t);
            }
        } else {
            renderer = new PageletRendererImpl();
            renderer.setIdentifier(id);
        }

        // Renderer url
        NodeList rendererUrlNodes = XPathHelper.selectList(node, "m:renderer", xpath);
        if (rendererUrlNodes.getLength() == 0)
            throw new IllegalStateException("Missing renderer in page template definition");
        for (int i = 0; i < rendererUrlNodes.getLength(); i++) {
            Node rendererUrlNode = rendererUrlNodes.item(i);
            URL rendererUrl = null;
            Node typeNode = rendererUrlNode.getAttributes().getNamedItem("type");
            String type = (typeNode != null) ? typeNode.getNodeValue() : RendererType.Page.toString();
            try {
                rendererUrl = new URL(rendererUrlNode.getFirstChild().getNodeValue());
                renderer.addRenderer(rendererUrl, type);
            } catch (MalformedURLException e) {
                throw new IllegalStateException(
                        "Malformed renderer url in page template definition: " + rendererUrlNode);
            }
        }

        // Composeable
        renderer.setComposeable("true".equals(XPathHelper.valueOf(node, "@composeable", xpath)));

        // Preview mode
        String previewMode = XPathHelper.valueOf(node, "m:preview", xpath);
        if (previewMode != null)
            renderer.setPreviewMode(PagePreviewMode.parse(previewMode));

        // Editor url
        String editorUrlNode = XPathHelper.valueOf(node, "m:editor", xpath);
        try {
            if (editorUrlNode != null) {
                URL editorUrl = new URL(editorUrlNode);
                renderer.setEditor(editorUrl);
            }
        } catch (MalformedURLException e) {
            throw new IllegalStateException("Malformed editor url in page template definition: " + editorUrlNode);
        }

        // client revalidation time
        String recheck = XPathHelper.valueOf(node, "m:recheck", xpath);
        if (recheck != null) {
            try {
                renderer.setClientRevalidationTime(ConfigurationUtils.parseDuration(recheck));
            } catch (NumberFormatException e) {
                throw new IllegalStateException(
                        "The pagelet renderer revalidation time is malformed: '" + recheck + "'");
            } catch (IllegalArgumentException e) {
                throw new IllegalStateException(
                        "The pagelet renderer revalidation time is malformed: '" + recheck + "'");
            }
        }

        // cache expiration time
        String valid = XPathHelper.valueOf(node, "m:valid", xpath);
        if (valid != null) {
            try {
                renderer.setCacheExpirationTime(ConfigurationUtils.parseDuration(valid));
            } catch (NumberFormatException e) {
                throw new IllegalStateException("The pagelet renderer valid time is malformed: '" + valid + "'", e);
            } catch (IllegalArgumentException e) {
                throw new IllegalStateException("The pagelet renderer valid time is malformed: '" + valid + "'", e);
            }
        }

        // name
        String name = XPathHelper.valueOf(node, "m:name", xpath);
        renderer.setName(name);

        // scripts
        NodeList scripts = XPathHelper.selectList(node, "m:includes/m:script", xpath);
        for (int i = 0; i < scripts.getLength(); i++) {
            renderer.addHTMLHeader(ScriptImpl.fromXml(scripts.item(i)));
        }

        // links
        NodeList includes = XPathHelper.selectList(node, "m:includes/m:link", xpath);
        for (int i = 0; i < includes.getLength(); i++) {
            renderer.addHTMLHeader(LinkImpl.fromXml(includes.item(i)));
        }

        return renderer;
    }

    /**
     * Returns an XML representation of this renderer.
     * 
     * @return the xml representation
     */
    public String toXml() {
        StringBuffer buf = new StringBuffer();
        buf.append("<pagelet");
        buf.append(" id=\"").append(identifier).append("\"");
        buf.append(" composeable=\"").append(composeable).append("\"");
        buf.append(">");

        // Names
        if (StringUtils.isNotBlank(name)) {
            buf.append("<name><![CDATA[");
            buf.append(name);
            buf.append("]]></name>");
        }

        // Renderer class
        if (!this.getClass().equals(PageletRendererImpl.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>");
        }

        // Editor url
        if (editor != null)
            buf.append("<editor>").append(editor.toExternalForm()).append("</editor>");

        // 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>");
        }

        // Preview
        if (!previewMode.equals(PagePreviewMode.None)) {
            buf.append("<preview>");
            buf.append(previewMode.toString().toLowerCase());
            buf.append("</preview>");
        }

        // 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("</pagelet>");
        return buf.toString();
    }

    /**
     * {@inheritDoc}
     * 
     * @see ch.entwine.weblounge.common.impl.content.GeneralComposeable#toString()
     */
    @Override
    public String toString() {
        return module.getIdentifier() + "/" + identifier;
    }

}