// ================================================================================================= // Copyright 2011 Twitter, Inc. // ------------------------------------------------------------------------------------------------- // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this work except in compliance with the License. // You may obtain a copy of the License in the LICENSE file, or 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. // ================================================================================================= package com.twitter.common.net.http.handlers; import com.google.common.base.Preconditions; import com.google.inject.BindingAnnotation; import com.twitter.common.base.ExceptionalClosure; import com.twitter.common.base.MorePreconditions; import org.antlr.stringtemplate.AutoIndentWriter; import org.antlr.stringtemplate.StringTemplate; import org.antlr.stringtemplate.StringTemplateGroup; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.logging.Level; import java.util.logging.Logger; /** * A base class for servlets that render using the string template templating system. Subclasses * can call one of the {@link #writeTemplate} methods to render their content with the associated * template. * * @author John Sirois */ public abstract class StringTemplateServlet extends HttpServlet { private static final String CONTENT_TYPE_TEXT_HTML = "text/html"; /** * A {@literal @BindingAnnotation} that allows configuration of whether or not * StringTemplateServlets should cache their templates. */ @BindingAnnotation @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER, ElementType.METHOD}) public @interface CacheTemplates {} private static final Logger LOG = Logger.getLogger(StringTemplateServlet.class.getName()); private final StringTemplateGroup group; private final String templatePath; /** * Creates a new StringTemplateServlet that expects to find its template located in the same * package on the classpath at '{@code templateName}.st'. * * @param templateName The name of the string template to use. * @param cacheTemplates {@code true} to re-use loaded templates, {@code false} to reload the * template for each request. */ protected StringTemplateServlet(String templateName, boolean cacheTemplates) { MorePreconditions.checkNotBlank(templateName); String templatePath = getClass().getPackage().getName().replace('.', '/') + "/" + templateName; StringTemplateGroup group = new StringTemplateGroup(templateName); Preconditions.checkNotNull(group.getInstanceOf(templatePath), "Failed to load template at: %s", templatePath); this.group = group; if (!cacheTemplates) { group.setRefreshInterval(0); } this.templatePath = templatePath; } protected final void writeTemplate(HttpServletResponse response, ExceptionalClosure<StringTemplate, ?> parameterSetter) throws IOException { writeTemplate(response, CONTENT_TYPE_TEXT_HTML, HttpServletResponse.SC_OK, parameterSetter); } protected final void writeTemplate(HttpServletResponse response, String contentType, int status, ExceptionalClosure<StringTemplate, ?> parameterSetter) throws IOException { Preconditions.checkNotNull(response); MorePreconditions.checkNotBlank(contentType); Preconditions.checkArgument(status > 0); Preconditions.checkNotNull(parameterSetter); StringTemplate stringTemplate = group.getInstanceOf(templatePath); try { parameterSetter.execute(stringTemplate); response.setStatus(status); response.setContentType(contentType); stringTemplate.write(new AutoIndentWriter(response.getWriter())); } catch (Exception e) { LOG.log(Level.SEVERE, "Unknown exception.", e); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } } }