blob: a4a59efd8537c389e537d22fa95890e858963d0c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. The ASF licenses this file to You
* under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License. For additional information regarding
* copyright in this work, please see the NOTICE file in the top level
* directory of this distribution.
*/
package org.apache.roller.weblogger.ui.rendering.velocity;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.roller.util.RollerConstants;
import org.apache.roller.weblogger.pojos.Template;
import org.apache.roller.weblogger.ui.rendering.Renderer;
import org.apache.roller.weblogger.ui.rendering.RenderingException;
import org.apache.roller.weblogger.ui.rendering.mobile.MobileDeviceRepository;
import org.apache.roller.weblogger.ui.rendering.model.UtilitiesModel;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.context.Context;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.exception.VelocityException;
/**
* Renderer that renders using the Velocity template engine.
*/
public class VelocityRenderer implements Renderer {
private static Log log = LogFactory.getLog(VelocityRenderer.class);
// the original template we are supposed to render
private Template renderTemplate = null;
private MobileDeviceRepository.DeviceType deviceType = null;
// the velocity templates
private org.apache.velocity.Template velocityTemplate = null;
private org.apache.velocity.Template velocityDecorator = null;
// a possible exception
private Exception velocityException = null;
public VelocityRenderer(Template template,
MobileDeviceRepository.DeviceType deviceType) throws Exception {
// the Template we are supposed to render
this.renderTemplate = template;
this.deviceType = deviceType;
try {
// make sure that we can locate the template
// if we can't then this will throw an exception
velocityTemplate = RollerVelocity.getTemplate(template.getId(),
deviceType, "UTF-8");
} catch (ResourceNotFoundException ex) {
// velocity couldn't find the resource so lets log a warning
log.warn("Error creating renderer for " + template.getId()
+ " due to [" + ex.getMessage() + "]");
// then just rethrow so that the caller knows this instantiation
// failed
throw ex;
} catch (ParseErrorException ex) {
// in the case of a parsing error we want to render an
// error page instead so the user knows what was wrong
velocityException = ex;
// need to lookup error page template
velocityTemplate = RollerVelocity.getTemplate("error-page.vm",
deviceType);
} catch (MethodInvocationException ex) {
// in the case of a invocation error we want to render an
// error page instead so the user knows what was wrong
velocityException = ex;
// need to lookup error page template
velocityTemplate = RollerVelocity.getTemplate("error-page.vm",
deviceType);
} catch (VelocityException ex) {
// in the case of a parsing error including a macro we want to
// render an error page instead so the user knows what was wrong
velocityException = ex;
// need to lookup error page template
velocityTemplate = RollerVelocity.getTemplate("error-page.vm",
deviceType);
} catch (Exception ex) {
// some kind of generic/unknown exception, dump it to the logs
log.error(
"Unknown exception creating renderer for "
+ template.getId(), ex);
// throw if back to the caller
throw ex;
}
}
/**
* @see org.apache.roller.weblogger.ui.rendering.Renderer#render(java.util.Map,
* java.io.Writer)
*/
public void render(Map<String, Object> model, Writer out)
throws RenderingException {
try {
if (velocityException != null) {
// Render exception
renderException(model, out, null);
// and we're done
return;
}
long startTime = System.currentTimeMillis();
// convert model to Velocity Context
Context ctx = new VelocityContext(model);
if (velocityDecorator != null) {
/**
* We only allow decorating once, so the process isn't fully
* recursive. This is just to keep it simple.
*/
// render base template to a temporary StringWriter
StringWriter sw = new StringWriter();
velocityTemplate.merge(ctx, sw);
// put rendered template into context
ctx.put("decorator_body", sw.toString());
log.debug("Applying decorator " + velocityDecorator.getName());
// now render decorator to our output writer
velocityDecorator.merge(ctx, out);
} else {
// no decorator, so just merge template to our output writer
velocityTemplate.merge(ctx, out);
}
long endTime = System.currentTimeMillis();
long renderTime = (endTime - startTime) / RollerConstants.SEC_IN_MS;
log.debug("Rendered [" + renderTemplate.getId() + "] in "
+ renderTime + " secs");
} catch (ParseErrorException ex) {
// in the case of a parsing error including a page we want to render
// an error on the page instead so the user knows what was wrong
velocityException = ex;
// need to lookup parse error template
renderException(model, out, "error-parse.vm");
} catch (MethodInvocationException ex) {
// in the case of a parsing error including a page we want to render
// an error on the page instead so the user knows what was wrong
velocityException = ex;
// need to lookup parse error template
renderException(model, out, "error-parse.vm");
} catch (VelocityException ex) {
// in the case of a parsing error including a macro we want to
// render an error page instead so the user knows what was wrong
velocityException = ex;
// need to lookup parse error template
renderException(model, out, "error-parse.vm");
} catch (Exception ex) {
// wrap and rethrow so caller can deal with it
throw new RenderingException("Error during rendering", ex);
}
}
/**
* Render Velocity Exception.
*
* @param model
* the model
* @param out
* the out
* @param template
* the template. Null if using existing template name
*
* @throws RenderingException
* the rendering exception
*/
private void renderException(Map<String, Object> model, Writer out,
String template) throws RenderingException {
try {
if (template != null) {
// need to lookup error page template
velocityTemplate = RollerVelocity.getTemplate(template,
deviceType);
}
Context ctx = new VelocityContext(model);
ctx.put("exception", velocityException);
ctx.put("exceptionSource", renderTemplate.getId());
ctx.put("exceptionDevice", deviceType);
ctx.put("utils", new UtilitiesModel());
// render output to Writer
velocityTemplate.merge(ctx, out);
} catch (Exception e) {
// wrap and rethrow so caller can deal with it
throw new RenderingException("Error during rendering", e);
}
}
}