/*
* 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 ch.entwine.weblounge.common.Times;
import ch.entwine.weblounge.common.content.RenderException;
import ch.entwine.weblounge.common.content.Renderer;
import ch.entwine.weblounge.common.impl.content.GeneralComposeable;
import ch.entwine.weblounge.common.impl.language.LanguageUtils;
import ch.entwine.weblounge.common.impl.request.SiteRequestWrapper;
import ch.entwine.weblounge.common.language.Language;
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.Site;
import ch.entwine.weblounge.common.url.UrlUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletResponse;
/**
* Thread safe base implementation for <code>PageTemplate</code>s and
* <code>PageletRenderer</code>s.
*/
public abstract class AbstractRenderer extends GeneralComposeable implements Renderer {
/** The logging facility */
private static final Logger logger = LoggerFactory.getLogger(AbstractRenderer.class);
/** Renderer URLs by type */
protected Map<String, URL> renderers = new HashMap<String, URL>(5);
/** The supported flavors */
protected Set<RequestFlavor> flavors = new HashSet<RequestFlavor>();
/**
* Creates a renderer with a recheck time of one day and a valid time of one
* week.
*/
public AbstractRenderer() {
super(Times.MS_PER_HOUR, Times.MS_PER_WEEK);
}
/**
* Creates a renderer with a recheck time of one day and a valid time of one
* week.
*
* @param identifier
* the renderer identifier
*/
public AbstractRenderer(String identifier) {
this(identifier, null);
}
/**
* Creates a renderer with a recheck time of one day and a valid time of one
* week.
*
* @param identifier
* the renderer identifier
* @param renderer
* url of the renderer
*/
public AbstractRenderer(String identifier, URL renderer) {
super(identifier, Times.MS_PER_HOUR, Times.MS_PER_WEEK);
this.renderers.put(RendererType.Page.toString().toLowerCase(), renderer);
}
/**
* Adds the given flavor to the list of supported flavors.
*
* @param flavor
* the supported flavor
* @see RequestFlavor
*/
public void addFlavor(RequestFlavor flavor) {
if (flavor == null)
throw new IllegalArgumentException("Flavor must not be null");
flavors.add(flavor);
}
/**
* Removes the specified flavor from the list of supported flavors.
*
* @param flavor
* the flavor
*/
public void removeFlavor(RequestFlavor flavor) {
if (flavor == null)
throw new IllegalArgumentException("Flavor must not be null");
flavors.remove(flavor);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Renderer#getFlavors()
*/
public RequestFlavor[] getFlavors() {
return flavors.toArray(new RequestFlavor[flavors.size()]);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Composeable#supportsFlavor(java.lang.String)
*/
public boolean supportsFlavor(RequestFlavor flavor) {
return flavors.contains(flavor);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Renderer#setRenderer(java.net.URL)
*/
public void setRenderer(URL renderer) {
addRenderer(renderer, RendererType.Page.toString());
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Renderer#addRenderer(java.net.URL,
* java.lang.String)
*/
public void addRenderer(URL renderer, String type) {
if (renderer == null)
throw new IllegalArgumentException("Renderer must not be null");
if (StringUtils.isBlank(type))
throw new IllegalArgumentException("Type must not be blank");
this.renderers.put(type.toLowerCase(), renderer);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Renderer#getRenderer()
*/
public URL getRenderer() {
return getRenderer(RendererType.Page.toString());
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Renderer#getRenderer(java.lang.String)
*/
public URL getRenderer(String type) {
if (StringUtils.isBlank(type))
throw new IllegalArgumentException("Type must not be blank");
return renderers.get(type.toLowerCase());
}
/**
* Convenience implementation for JSP renderer. The <code>renderer</code> url
* is first looked up using the available language information from request
* and site. Then it is included in the response.
*
* @param request
* the request
* @param response
* the response
* @param renderer
* the renderer
* @throws RenderException
* if an error occurs while rendering
*/
protected void includeJSP(WebloungeRequest request,
WebloungeResponse response, URL renderer) throws RenderException {
Site site = request.getSite();
Language language = request.getLanguage();
File jsp = null;
try {
if ("file".equals(renderer.getProtocol())) {
// Find the best match for the template
String[] filePaths = LanguageUtils.getLanguageVariants(renderer.toExternalForm(), language, site.getDefaultLanguage());
for (String path : filePaths) {
logger.trace("Looking for jsp {}", path);
File f = new File(path);
if (f.exists()) {
logger.debug("Found jsp at {}", path);
jsp = f;
break;
}
}
// Did we find a suitable JSP?
if (jsp == null) {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
throw new RenderException(this, "No suitable java server page found for " + renderer + " and language '" + language.getIdentifier() + "'");
}
// Check readability
if (!jsp.canRead()) {
response.sendError(HttpServletResponse.SC_FORBIDDEN);
throw new RenderException(this, "Java server page at " + jsp + " cannot be read");
}
// No directory listings allowed
if (!jsp.isFile()) {
response.sendError(HttpServletResponse.SC_FORBIDDEN);
throw new RenderException(this, "Java server page at " + jsp + " is not a file");
}
renderer = jsp.toURI().toURL();
}
// Prepare a request to site resources
String servletPath = "/weblounge-sites/" + site.getIdentifier();
String requestPath = renderer.getPath();
requestPath = requestPath.substring(servletPath.length());
requestPath = UrlUtils.concat(Site.BUNDLE_PATH, requestPath);
SiteRequestWrapper siteRequest = new SiteRequestWrapper(request, requestPath, false);
RequestDispatcher dispatcher = request.getRequestDispatcher(servletPath);
if (dispatcher == null)
throw new IllegalStateException("No dispatcher found for site '" + site + "'");
// Finally serve the JSP
logger.debug("Including jsp {}", renderer);
dispatcher.include(siteRequest, response);
response.getWriter().flush();
} catch (IOException e) {
logger.error("Exception while including jsp {}", renderer, e);
} catch (Throwable t) {
throw new RenderException(this, t);
}
}
}