Skip to content

Instantly share code, notes, and snippets.

@serac
Created March 15, 2016 11:33
Show Gist options
  • Select an option

  • Save serac/211c65de13eec4914cf0 to your computer and use it in GitHub Desktop.

Select an option

Save serac/211c65de13eec4914cf0 to your computer and use it in GitHub Desktop.
Recipe for Logging Rendered Velocity Template
/* See LICENSE for licensing and NOTICE for copyright. */
package edu.vt.middleware.ed.ws.soap.mvc;
import java.io.Writer;
import java.lang.reflect.Constructor;
import javax.servlet.http.HttpServletResponse;
import org.apache.velocity.Template;
import org.apache.velocity.context.Context;
import org.apache.velocity.exception.MethodInvocationException;
import org.springframework.web.servlet.view.velocity.VelocityView;
import org.springframework.web.util.NestedServletException;
/**
* Velocity view that allows setting the {@link java.io.Writer} class used to render the template.
*
* @author Middleware Services
*/
public class ConfigurableVelocityView extends VelocityView
{
/** Writer class. */
private Class<? extends Writer> writerClass;
/**
* Sets the {@link java.io.Writer} class used to render the Velocity template.
*
* @param clazz Writer class whose constructor takes exactly one argument, the {@link
* javax.servlet.http.HttpServletResponse#getWriter()} to wrap.
*/
public void setWriterClass(final Class<? extends Writer> clazz)
{
writerClass = clazz;
}
@Override
protected void mergeTemplate(final Template template, final Context context, final HttpServletResponse response)
throws Exception
{
final Writer writer = newWriter(response.getWriter());
try {
template.merge(context, writer);
} catch (MethodInvocationException ex) {
final Throwable cause = ex.getWrappedThrowable();
throw new NestedServletException(
"Method invocation failed during rendering of Velocity view named '" + getBeanName() + "': " + ex.getMessage() +
"; reference [" + ex.getReferenceName() + "], method '" + ex.getMethodName() + "'",
cause == null ? ex : cause);
} finally {
writer.flush();
}
}
/**
* Creates a new instance of the configured writer class.
*
* @param wrapped Writer to wrap.
*
* @return New wrapped writer instance.
*
* @throws Exception On errors creating the writer.
*/
private Writer newWriter(final Writer wrapped)
throws Exception
{
if (writerClass == null) {
return wrapped;
}
final Constructor<? extends Writer> constructor = writerClass.getConstructor(Writer.class);
return constructor.newInstance(wrapped);
}
}
/* See LICENSE for licensing and NOTICE for copyright. */
package edu.vt.middleware.ed.ws.soap.mvc;
import java.io.Writer;
import org.springframework.web.servlet.view.AbstractTemplateViewResolver;
/**
* View resolver for use with {@link ConfigurableVelocityView}.
*
* @author Middleware Services
*/
public class ConfigurableVelocityViewResolver extends AbstractTemplateViewResolver
{
/** Writer class. */
private final Class<? extends Writer> writerClass;
/** Creates a new instance. */
public ConfigurableVelocityViewResolver()
{
this(null);
}
/**
* Creates a new instance that uses the given writer class for rendering the Velocity template.
*
* @param clazz Writer class whose constructor takes exactly one argument, the {@link
* javax.servlet.http.HttpServletResponse#getWriter()} to wrap.
*/
public ConfigurableVelocityViewResolver(final Class<? extends Writer> clazz)
{
writerClass = clazz;
setViewClass(requiredViewClass());
}
/** @return <code>XmlStripVelocityView.class.</code> */
@Override
protected Class requiredViewClass()
{
return ConfigurableVelocityView.class;
}
@Override
protected ConfigurableVelocityView buildView(final String viewName)
throws Exception
{
final ConfigurableVelocityView view = (ConfigurableVelocityView) super.buildView(viewName);
view.setWriterClass(writerClass);
return view;
}
}
/* See LICENSE for licensing and NOTICE for copyright. */
package edu.vt.middleware.ed.ws.soap.mvc;
import java.io.FilterWriter;
import java.io.IOException;
import java.io.Writer;
import java.nio.CharBuffer;
import edu.vt.middleware.ed.util.logging.Logger;
import edu.vt.middleware.ed.util.logging.LoggerFactory;
/**
* Buffers data passed to <code>write</code> methods and writes the data to DEBUG logger on {@link #flush()}.
*
* @author Middleware Services
*/
public class LoggingWriter extends FilterWriter
{
/** Logger instance. */
private final Logger logger = LoggerFactory.getLogger(LoggingWriter.class);
/** Output buffer. */
private CharBuffer buffer;
/**
* Create a new instance around the given writer.
*
* @param out a Writer object to provide the underlying stream.
*/
public LoggingWriter(final Writer out)
{
super(out);
if (logger.isDebugEnabled()) {
buffer = CharBuffer.allocate(2048);
}
}
@Override
public void write(final int c)
throws IOException
{
if (buffer != null) {
if (!buffer.hasRemaining()) {
growBuffer();
}
buffer.put((char) c);
}
super.write(c);
}
@Override
public void write(final char[] cbuf, final int off, final int len)
throws IOException
{
if (buffer != null) {
if (buffer.limit() - buffer.position() < len) {
growBuffer();
}
buffer.put(cbuf, off, len);
}
super.write(cbuf, off, len);
}
@Override
public void write(final String str, final int off, final int len)
throws IOException
{
if (buffer != null) {
if (buffer.limit() - buffer.position() < len) {
growBuffer();
}
buffer.put(str, off, len);
}
super.write(str, off, len);
}
@Override
public void flush()
throws IOException
{
super.flush();
if (buffer != null) {
buffer.flip();
logger.debug("Written data:\n{}", buffer);
buffer.clear();
}
}
/** Allocates a new buffer of 2x capacity of current and copies content over. */
private void growBuffer()
{
final CharBuffer tmp = CharBuffer.allocate(buffer.capacity() * 2);
buffer.flip();
tmp.put(buffer);
buffer = tmp;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- Other beans as necessary -->
<bean id="loggingViewResolver"
class="edu.vt.middleware.ed.ws.soap.mvc.ConfigurableVelocityViewResolver"
parent="velocityViewResolver"
c:clazz="edu.vt.middleware.ed.ws.soap.mvc.LoggingWriter"
p:prefix="template-path/"
p:contentType="text/plain;charset=UTF-8"/>
</beans>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment