Repository: incubator-freemarker Updated Branches: refs/heads/2.3-gae 2531f51e7 -> fca65a24f
Continued FREEMARKER-1 "Option to not to overwrite response ContentType in FreemarkerServlet": 3 possible values for OverrideResponseContentType init-param. Early version of the ResponseCharacterEncoding init-param. More concise tests. Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/fca65a24 Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/fca65a24 Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/fca65a24 Branch: refs/heads/2.3-gae Commit: fca65a24f481d657bf8676485dbdf842c2bb562e Parents: 2531f51 Author: ddekany <[email protected]> Authored: Thu Oct 22 01:55:18 2015 +0200 Committer: ddekany <[email protected]> Committed: Thu Oct 22 02:33:22 2015 +0200 ---------------------------------------------------------------------- .../ext/servlet/FreemarkerServlet.java | 187 +++++++++--- src/manual/book.xml | 19 +- .../ext/servlet/FreemarkerServletTest.java | 296 +++++++------------ 3 files changed, 270 insertions(+), 232 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/fca65a24/src/main/java/freemarker/ext/servlet/FreemarkerServlet.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/ext/servlet/FreemarkerServlet.java b/src/main/java/freemarker/ext/servlet/FreemarkerServlet.java index b4aa296..c344bb4 100644 --- a/src/main/java/freemarker/ext/servlet/FreemarkerServlet.java +++ b/src/main/java/freemarker/ext/servlet/FreemarkerServlet.java @@ -38,8 +38,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import com.sun.org.apache.xml.internal.serialize.OutputFormat; - import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import freemarker.cache.ClassTemplateLoader; import freemarker.cache.FileTemplateLoader; @@ -47,6 +45,7 @@ import freemarker.cache.MultiTemplateLoader; import freemarker.cache.TemplateLoader; import freemarker.cache.WebappTemplateLoader; import freemarker.core.Configurable; +import freemarker.core.OutputFormat; import freemarker.core.UndefinedOutputFormat; import freemarker.ext.jsp.TaglibFactory; import freemarker.ext.jsp.TaglibFactory.ClasspathMetaInfTldSource; @@ -141,27 +140,34 @@ import freemarker.template.utility.StringUtil; * when nothing else specifies the MIME type. The things that may specify the MIME type (and hence this init-param is * ignored), starting with the highest precedence, are: * <ol> + * <li>If the {@value #INIT_PARAM_OVERRIDE_RESPONSE_CONTENT_TYPE} init-param is {@value #INIT_PARAM_VALUE_NEVER} (it + * itn't be default), then the value of {@link HttpServletResponse#getContentType()} is if that's non-{@code null}. * <li>The template's custom attribute name <tt>content_type</tt> in the <tt>attributes</tt> parameter of the * <tt><#ftl></tt> directive. This is a legacy feature, deprecated by the {@link OutputFormat} mechanism. * <li>The {@linkplain Template#getOutputFormat() output format of the template}, if that has non-{@code null} MIME-type - * ({@link OutputFormat#getMediaType()}). When a template has no output format specified, {@link UndefinedOutputFormat} + * ({@link OutputFormat#getMimeType()}). When a template has no output format specified, {@link UndefinedOutputFormat} * is used, which has {@code null} MIME-type. (The output format of a template is deduced from {@link Configuration} * settings, or can be specified directly in the template, like {@code <#ftl outputFormat="HTML">}. See the FreeMarker - * Manual for more about the output format mechanism. Note that setting an output format may turns on auto-escaping, - * so it's not just about MIME types.) - * <li>If the {@value #INIT_PARAM_OVERRIDE_RESPONSE_CONTENT_TYPE} init-param is {@code false} (the default is - * {@code true}), then the value of {@link HttpServletResponse#getContentType()} is used if that's non-{@code null}. + * Manual for more about the output format mechanism. Note that setting an output format may turns on auto-escaping, so + * it's not just about MIME types.) + * <li>If the {@value #INIT_PARAM_OVERRIDE_RESPONSE_CONTENT_TYPE} init-param is not {@value #INIT_PARAM_VALUE_ALWAYS} + * (the default is {@value #INIT_PARAM_VALUE_ALWAYS}), then the value of {@link HttpServletResponse#getContentType()} is + * used if that's non-{@code null}. * </ol> - * If none of the above gives a MIME type, then this init-param does. Defaults to <tt>"text/html"</tt>. The - * value may include the charset (e.g. <tt>"text/html; charset=utf-8"</tt>). If the charset is not specified in this - * init-param, then the charset (encoding) of the actual template file will appended after it, which, as per the - * Servlet specification, also sets the actual encoding used to write the response body.</li> + * If none of the above gives a MIME type, then this init-param does. Defaults to <tt>"text/html"</tt>. The value may + * include the charset (e.g. <tt>"text/html; charset=utf-8"</tt>). If the charset is not specified in this init-param, + * then the charset (encoding) of the actual template file will appended after it, which, as per the Servlet + * specification, also sets the actual encoding used to write the response body.</li> * - * <li><strong>{@value #INIT_PARAM_OVERRIDE_RESPONSE_CONTENT_TYPE}</strong> (since 2.3.24): Specifies if we should - * always set the {@code contentType} in the {@link HttpServletResponse} to the value of the - * {@value #INIT_PARAM_CONTENT_TYPE} init-param (or to its default, {@code text/html}), or only if it wasn't already set - * (i.e., {@link HttpServletResponse#getContentType()} returns {@code null}). The default is {@code true}. Setting this - * to {@code false} allows you to specify the content type before forwarding to {@link FreemarkerServlet}.</li> + * <li><strong>{@value #INIT_PARAM_OVERRIDE_RESPONSE_CONTENT_TYPE}</strong> (since 2.3.24): Specifies when should we + * override the {@code contentType} that's already set (i.e., non-{@code null}) in the {@link HttpServletResponse}. The + * default is {@value #INIT_PARAM_VALUE_ALWAYS}, which means that we always set the content type. Another possible value + * is {@value #INIT_PARAM_VALUE_NEVER}, which means that we don't set the content type in the response, unless + * {@link HttpServletResponse#getContentType()} is {@code null}. The third possible value is + * {@value #INIT_PARAM_VALUE_WHEN_TEMPLATE_HAS_MIME_TYPE}, which means that we only set the content type if either the + * template has an associated {@link OutputFormat} with non-{@code null} {@link OutputFormat#getMimeType()}, or it has a + * custom attribute with name <tt>content_type</tt>, or {@link HttpServletResponse#getContentType()} is {@code null}. + * Setting this init-param allows you to specify the content type before forwarding to {@link FreemarkerServlet}.</li> * * <li><strong>{@value #INIT_PARAM_BUFFER_SIZE}</strong>: Sets the size of the output buffer in bytes, or if "KB" or * "MB" is written after the number (like {@code <param-value>256 KB</param-value>}) then in kilobytes or megabytes. @@ -300,6 +306,13 @@ public class FreemarkerServlet extends HttpServlet { /** * Init-param name - see the {@link FreemarkerServlet} class documentation about the init-params. + * + * @since 2.3.24 + */ + public static final String INIT_PARAM_RESPONSE_CHARACTER_ENCODING = "ResponseCharacterEncoding"; + + /** + * Init-param name - see the {@link FreemarkerServlet} class documentation about the init-params. * * @since 2.3.22 */ @@ -342,6 +355,13 @@ public class FreemarkerServlet extends HttpServlet { private static final String DEPR_INITPARAM_DEBUG = "debug"; static final String DEFAULT_CONTENT_TYPE = "text/html"; + + public static final String INIT_PARAM_VALUE_NEVER = "never"; + public static final String INIT_PARAM_VALUE_ALWAYS = "always"; + public static final String INIT_PARAM_VALUE_WHEN_TEMPLATE_HAS_MIME_TYPE = "whenTemplateHasMimeType"; + public static final String INIT_PARAM_VALUE_FROM_TEMPLATE = "fromTemplate"; + public static final String INIT_PARAM_VALUE_LEGACY = "legacy"; + public static final String INIT_PARAM_VALUE_DO_NOT_SET = "doNotSet"; /** * When set, the items defined in it will be added after those coming from the @@ -439,8 +459,9 @@ public class FreemarkerServlet extends HttpServlet { @SuppressFBWarnings(value="SE_BAD_FIELD", justification="Not investing into making this Servlet serializable") private ObjectWrapper wrapper; private String contentType; - private boolean overrideResponseContentType = true; - private boolean noCharsetInContentType; + private OverrideResponseContentType overrideResponseContentType = OverrideResponseContentType.ALWAYS; + private ResponseCharacterEncoding responseCharacterEncoding = ResponseCharacterEncoding.LEGACY; + private boolean contentTypeContainsCharset; private List/*<MetaInfTldSource>*/ metaInfTldSources; private List/*<String>*/ classpathTlds; @@ -586,7 +607,9 @@ public class FreemarkerServlet extends HttpServlet { } else if (name.equals(INIT_PARAM_CONTENT_TYPE)) { contentType = value; } else if (name.equals(INIT_PARAM_OVERRIDE_RESPONSE_CONTENT_TYPE)) { - overrideResponseContentType = StringUtil.getYesNo(value); + overrideResponseContentType = initParamValueToEnum(value, OverrideResponseContentType.values()); + } else if (name.equals(INIT_PARAM_RESPONSE_CHARACTER_ENCODING)) { + responseCharacterEncoding = initParamValueToEnum(value, ResponseCharacterEncoding.values()); } else if (name.equals(INIT_PARAM_EXCEPTION_ON_MISSING_TEMPLATE)) { exceptionOnMissingTemplate = StringUtil.getYesNo(value); } else if (name.equals(INIT_PARAM_META_INF_TLD_LOCATIONS)) {; @@ -608,20 +631,24 @@ public class FreemarkerServlet extends HttpServlet { } } // while initpnames - noCharsetInContentType = true; - int i = contentType.toLowerCase().indexOf("charset="); - if (i != -1) { + contentTypeContainsCharset = contentTypeContainsCharset(contentType); + } + + private boolean contentTypeContainsCharset(String contentType) { + int charsetIdx = contentType.toLowerCase().indexOf("charset="); + if (charsetIdx != -1) { char c = 0; - i--; - while (i >= 0) { - c = contentType.charAt(i); + charsetIdx--; + while (charsetIdx >= 0) { + c = contentType.charAt(charsetIdx); if (!Character.isWhitespace(c)) break; - i--; + charsetIdx--; } - if (i == -1 || c == ';') { - noCharsetInContentType = false; + if (charsetIdx == -1 || c == ';') { + return true; } } + return false; } private List/*<MetaInfTldSource>*/ parseAsMetaInfTldLocations(String value) throws ParseException { @@ -739,18 +766,26 @@ public class FreemarkerServlet extends HttpServlet { "Unexpected error when loading template " + StringUtil.jQuoteNoXSS(templatePath) + ".", e); } - String templateSpecificContentType = getTemplateSpecificContentType(template); - if (templateSpecificContentType != null) { - response.setContentType(templateSpecificContentType); - } else { - if (overrideResponseContentType || response.getContentType() == null) { - if (noCharsetInContentType) { - response.setContentType(contentType + "; charset=" + template.getEncoding()); + if (response.getContentType() == null || overrideResponseContentType != OverrideResponseContentType.NEVER) { + String templateSpecificContentType = getTemplateSpecificContentType(template); + if (templateSpecificContentType != null) { + response.setContentType(templateSpecificContentType); + } else if (response.getContentType() == null + || overrideResponseContentType == OverrideResponseContentType.ALWAYS) { + if (!contentTypeContainsCharset && responseCharacterEncoding == ResponseCharacterEncoding.LEGACY) { + response.setContentType(contentType + "; charset=" + getTemplateSpecificOutputEncoding(template)); } else { response.setContentType(contentType); } } } + + if (responseCharacterEncoding != ResponseCharacterEncoding.LEGACY + && responseCharacterEncoding != ResponseCharacterEncoding.DO_NOT_SET) { + // Using the Servlet 2.4 way of setting character encoding. + response.setCharacterEncoding(getTemplateSpecificOutputEncoding(template)); + // TODO handle "always ${charset}" + } setBrowserCachingPolicy(response); @@ -781,15 +816,26 @@ public class FreemarkerServlet extends HttpServlet { } } + private String getTemplateSpecificOutputEncoding(Template template) { + String outputEncoding = responseCharacterEncoding == ResponseCharacterEncoding.LEGACY ? null + : template.getOutputEncoding(); + return outputEncoding != null ? outputEncoding : template.getEncoding(); + } + private String getTemplateSpecificContentType(final Template template) { Object contentTypeAttr = template.getCustomAttribute("content_type"); if (contentTypeAttr != null) { - // Convert with toString() for backward compatibility + // Converted with toString() for backward compatibility. + // Don't add charset for backward compatibility. return contentTypeAttr.toString(); } String outputFormatMimeType = template.getOutputFormat().getMimeType(); if (outputFormatMimeType != null) { + if (responseCharacterEncoding == ResponseCharacterEncoding.LEGACY) { + // In legacy mode we won't call serlvetResponse.getCharacterEncoding(...), so: + outputFormatMimeType += "; charset=" + getTemplateSpecificOutputEncoding(template); + } return outputFormatMimeType; } @@ -842,7 +888,7 @@ public class FreemarkerServlet extends HttpServlet { * to use the locale indicated in the request. * * @param templatePath - * The template path (templat name) as it will be passed to {@link Configuration#getTemplate(String)}. + * The template path (template name) as it will be passed to {@link Configuration#getTemplate(String)}. * (Not to be confused with the servlet init-param of identical name; they aren't related.) * * @throws ServletException @@ -1398,4 +1444,71 @@ public class FreemarkerServlet extends HttpServlet { } + private <T extends InitParamValueEnum> T initParamValueToEnum(String initParamValue, T[] values) { + for (T value : values) { + if (initParamValue.equals(value.getInitParamValue())) { + return value; + } + } + + StringBuilder sb = new StringBuilder(); + sb.append(StringUtil.jQuote(initParamValue)); + sb.append(" is not a one of the enumeration values: "); + boolean first = true; + for (T value : values) { + if (!first) { + sb.append(", "); + } else { + first = false; + } + sb.append(StringUtil.jQuote(value.getInitParamValue())); + } + throw new IllegalArgumentException(sb.toString()); + } + + /** + * Superclass of all (future) init-param value enums. + * + * @see #initParamValueToEnum + */ + private interface InitParamValueEnum { + String getInitParamValue(); + } + + private enum OverrideResponseContentType implements InitParamValueEnum { + ALWAYS(INIT_PARAM_VALUE_ALWAYS), + NEVER(INIT_PARAM_VALUE_NEVER), + WHEN_TEMPLATE_HAS_MIME_TYPE(INIT_PARAM_VALUE_WHEN_TEMPLATE_HAS_MIME_TYPE); + + private final String initParamValue; + + OverrideResponseContentType(String initParamValue) { + this.initParamValue = initParamValue; + } + + @Override + public String getInitParamValue() { + return initParamValue; + } + } + + private enum ResponseCharacterEncoding implements InitParamValueEnum { + LEGACY(INIT_PARAM_VALUE_LEGACY), + FROM_TEMPLATE(INIT_PARAM_VALUE_FROM_TEMPLATE), + DO_NOT_SET(INIT_PARAM_VALUE_DO_NOT_SET); + // TODO: "always ${charset}" + + private final String initParamValue; + + ResponseCharacterEncoding(String initParamValue) { + this.initParamValue = initParamValue; + } + + @Override + public String getInitParamValue() { + return initParamValue; + } + + } + } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/fca65a24/src/manual/book.xml ---------------------------------------------------------------------- diff --git a/src/manual/book.xml b/src/manual/book.xml index 58bfc7a..8030ffb 100644 --- a/src/manual/book.xml +++ b/src/manual/book.xml @@ -26039,16 +26039,15 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting> <listitem> <para>New <literal>FreemarkerServlet</literal> init-param: - <literal>OverrideResponseContentType</literal>. This specifies - if we should always set the <literal>contentType</literal> in - the <literal>HttpServletResponse</literal> (to the value of the - to the value of the <literal>ContentType</literal> init-param), - or only if it wasn't already set. The default is - <literal>true</literal>, which gives the backward compatible - behavior. Now that this init-param exists, you can disable this - behavior, so the <literal>contentType</literal> you have - specified before forwarding to - <literal>FreemarkerServlet</literal> wins.</para> + <literal>OverrideResponseContentType</literal>. Specifies when + should we override the <literal>contentType</literal> that's + already set (i.e., non-<literal>null</literal>) in the + <literal>HttpServletResponse</literal>. Earlier, we have always + set it, and that's still the default behavior. But now that this + init-param exists, you can change that behavior, so that the + <literal>contentType</literal> you have specified before + forwarding to <literal>FreemarkerServlet</literal> + matters.</para> </listitem> <listitem> http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/fca65a24/src/test/java/freemarker/ext/servlet/FreemarkerServletTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/freemarker/ext/servlet/FreemarkerServletTest.java b/src/test/java/freemarker/ext/servlet/FreemarkerServletTest.java index 5bb8dc9..5f7e699 100644 --- a/src/test/java/freemarker/ext/servlet/FreemarkerServletTest.java +++ b/src/test/java/freemarker/ext/servlet/FreemarkerServletTest.java @@ -18,6 +18,7 @@ */ package freemarker.ext.servlet; +import static freemarker.ext.servlet.FreemarkerServlet.*; import static org.junit.Assert.*; import java.io.IOException; @@ -32,213 +33,138 @@ import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockServletConfig; import org.springframework.mock.web.MockServletContext; -import org.springframework.util.Assert; -import freemarker.log.Logger; +import freemarker.template.Configuration; public class FreemarkerServletTest { - private static final Logger LOG = Logger.getLogger("freemarker.servlet"); - private static final String TEST_TEMPLATE_PATH = "classpath:freemarker/ext/servlet"; private MockServletContext servletContext; @Before - public void setUp() throws ServletException, IOException { + public void setUp() throws Exception { servletContext = new MockServletContext(); servletContext.setContextPath("/"); } @Test - public void testContentTypeInitParams_withNoResponseContentType_DefaultOverriding() throws ServletException, IOException { - MockHttpServletRequest request = createMockHttpServletRequest(servletContext, "/foo.ftl"); - MockHttpServletResponse response = new MockHttpServletResponse(); - assertNull(response.getContentType()); - - createFreemarkerServlet().doGet(request, response); - LOG.debug("response content: " + response.getContentAsString()); - - assertEquals(HttpServletResponse.SC_OK, response.getStatus()); - assertTrue(response.getContentType().contains(FreemarkerServlet.DEFAULT_CONTENT_TYPE)); - } - - @Test - public void testContentTypeInitParams_withNoResponseContentType_DefaultOverriding2() throws ServletException, IOException { - MockHttpServletRequest request = createMockHttpServletRequest(servletContext, "/foo.ftl"); - MockHttpServletResponse response = new MockHttpServletResponse(); - assertNull(response.getContentType()); - - createFreemarkerServlet( - FreemarkerServlet.INIT_PARAM_CONTENT_TYPE, "text/css") - .doGet(request, response); - LOG.debug("response content: " + response.getContentAsString()); - - assertEquals(HttpServletResponse.SC_OK, response.getStatus()); - assertTrue(response.getContentType().contains("text/css")); - } - - @Test - public void testContentTypeInitParams_withResponseContentType_DefaultOverriding() throws ServletException, IOException { - MockHttpServletRequest request = createMockHttpServletRequest(servletContext, "/foo.ftl"); - MockHttpServletResponse response = new MockHttpServletResponse(); - response.setContentType("application/json"); - assertEquals("application/json", response.getContentType()); - - createFreemarkerServlet().doGet(request, response); - LOG.debug("response content: " + response.getContentAsString()); - - assertEquals(HttpServletResponse.SC_OK, response.getStatus()); - assertTrue(response.getContentType().contains(FreemarkerServlet.DEFAULT_CONTENT_TYPE)); - } - - @Test - public void testContentTypeInitParams_withResponseContentType_DefaultOverriding2() throws ServletException, IOException { - MockHttpServletRequest request = createMockHttpServletRequest(servletContext, "/foo.ftl"); - MockHttpServletResponse response = new MockHttpServletResponse(); - response.setContentType("application/json"); - assertEquals("application/json", response.getContentType()); - - createFreemarkerServlet( - FreemarkerServlet.INIT_PARAM_CONTENT_TYPE, "text/css") - .doGet(request, response); - LOG.debug("response content: " + response.getContentAsString()); - - assertEquals(HttpServletResponse.SC_OK, response.getStatus()); - assertTrue(response.getContentType().contains("text/css")); - } - - @Test - public void testContentTypeInitParams_withResponseContentType_NoOverriding() throws ServletException, IOException { - MockHttpServletRequest request = createMockHttpServletRequest(servletContext, "/foo.ftl"); - MockHttpServletResponse response = new MockHttpServletResponse(); - response.setContentType("application/json"); - assertEquals("application/json", response.getContentType()); - - createFreemarkerServlet(FreemarkerServlet.INIT_PARAM_OVERRIDE_RESPONSE_CONTENT_TYPE, "false") - .doGet(request, response); - LOG.debug("response content: " + response.getContentAsString()); - - assertEquals(HttpServletResponse.SC_OK, response.getStatus()); - assertEquals("application/json", response.getContentType()); - } - - @Test - public void testContentTypeInitParams_withResponseContentType_NoOverriding2() throws ServletException, IOException { - MockHttpServletRequest request = createMockHttpServletRequest(servletContext, "/foo.ftl"); - MockHttpServletResponse response = new MockHttpServletResponse(); - response.setContentType("application/json"); - assertEquals("application/json", response.getContentType()); - - createFreemarkerServlet( - FreemarkerServlet.INIT_PARAM_OVERRIDE_RESPONSE_CONTENT_TYPE, "false", - FreemarkerServlet.INIT_PARAM_CONTENT_TYPE, "text/css") - .doGet(request, response); - LOG.debug("response content: " + response.getContentAsString()); - - assertEquals(HttpServletResponse.SC_OK, response.getStatus()); - assertEquals("application/json", response.getContentType()); - } - - @Test - public void testContentTypeInitParams_withNoResponseContentType_NoOverriding() throws ServletException, IOException { - MockHttpServletRequest request = createMockHttpServletRequest(servletContext, "/foo.ftl"); - MockHttpServletResponse response = new MockHttpServletResponse(); - assertNull(response.getContentType()); - - createFreemarkerServlet(FreemarkerServlet.INIT_PARAM_OVERRIDE_RESPONSE_CONTENT_TYPE, "false") - .doGet(request, response); - LOG.debug("response content: " + response.getContentAsString()); - - assertEquals(HttpServletResponse.SC_OK, response.getStatus()); - assertTrue(response.getContentType().contains(FreemarkerServlet.DEFAULT_CONTENT_TYPE)); - } - - @Test - public void testContentTypeInitParams_withNoResponseContentType_NoOverriding2() throws ServletException, IOException { - MockHttpServletRequest request = createMockHttpServletRequest(servletContext, "/foo.ftl"); - MockHttpServletResponse response = new MockHttpServletResponse(); - assertNull(response.getContentType()); - - createFreemarkerServlet( - FreemarkerServlet.INIT_PARAM_OVERRIDE_RESPONSE_CONTENT_TYPE, "false", - FreemarkerServlet.INIT_PARAM_CONTENT_TYPE, "text/css") - .doGet(request, response); - LOG.debug("response content: " + response.getContentAsString()); - - assertEquals(HttpServletResponse.SC_OK, response.getStatus()); - assertTrue(response.getContentType().contains("text/css")); - } - - @Test - public void testContentTypeInitParams_ftlAttrAlwaysWins_DefaultOverriding() throws ServletException, IOException { - MockHttpServletRequest request = createMockHttpServletRequest(servletContext, "/contentTypeAttr.ftl"); - MockHttpServletResponse response = new MockHttpServletResponse(); - response.setContentType("application/json"); - assertEquals("application/json", response.getContentType()); - - createFreemarkerServlet().doGet(request, response); - LOG.debug("response content: " + response.getContentAsString()); - - assertEquals(HttpServletResponse.SC_OK, response.getStatus()); - assertEquals("text/plain", response.getContentType()); - } - - @Test - public void testContentTypeInitParams_ftlAttrAlwaysWins_NoOverriding() throws ServletException, IOException { - MockHttpServletRequest request = createMockHttpServletRequest(servletContext, "/contentTypeAttr.ftl"); - MockHttpServletResponse response = new MockHttpServletResponse(); - response.setContentType("application/json"); - assertEquals("application/json", response.getContentType()); - - createFreemarkerServlet(FreemarkerServlet.INIT_PARAM_OVERRIDE_RESPONSE_CONTENT_TYPE, "false") - .doGet(request, response); - LOG.debug("response content: " + response.getContentAsString()); - - assertEquals(HttpServletResponse.SC_OK, response.getStatus()); - assertEquals("text/plain", response.getContentType()); - } - - @Test - public void testContentTypeInitParams_outputFormatAlwaysWins_DefaultOverriding() throws ServletException, IOException { - MockHttpServletRequest request = createMockHttpServletRequest(servletContext, "/outputFormatHeader.ftl"); - MockHttpServletResponse response = new MockHttpServletResponse(); - response.setContentType("application/json"); - assertEquals("application/json", response.getContentType()); - - createFreemarkerServlet().doGet(request, response); - LOG.debug("response content: " + response.getContentAsString()); - - assertEquals(HttpServletResponse.SC_OK, response.getStatus()); - assertEquals("text/plain", response.getContentType()); + public void testContentTypeInitParams() throws Exception { + // Default is INIT_PARAM_VALUE_ALWAYS, hence null is the same: + for (String overrideCT : new String[] { null, INIT_PARAM_VALUE_ALWAYS }) { + assertResponseContentTypeEquals( + DEFAULT_CONTENT_TYPE + "; charset=UTF-8", // <- expected + null, overrideCT, // <- init-params + "foo.ftl", null); // <- request + assertResponseContentTypeEquals( + "text/css; charset=UTF-8", // <- expected + "text/css", overrideCT, // <- init-params + "foo.ftl", null); // <- request + assertResponseContentTypeEquals( + DEFAULT_CONTENT_TYPE + "; charset=UTF-8", // <- expected + null, overrideCT, // <- init-params + "foo.ftl", "application/json"); // <- request + assertResponseContentTypeEquals( + "text/css; charset=UTF-8", // <- expected + "text/css", overrideCT, // <- init-params + "foo.ftl", "application/json"); // <- request + assertResponseContentTypeEquals( + "text/plain", // <- expected + null, overrideCT, // <- init-params + "contentTypeAttr.ftl", "application/json"); // <- request + assertResponseContentTypeEquals( + "text/plain; charset=UTF-8", // <- expected + null, overrideCT, // <- init-params + "outputFormatHeader.ftl", "application/json"); // <- request + } + + assertResponseContentTypeEquals( + DEFAULT_CONTENT_TYPE + "; charset=UTF-8", // <- expected + null, INIT_PARAM_VALUE_WHEN_TEMPLATE_HAS_MIME_TYPE, // <- init-params + "foo.ftl", null); // <- request + assertResponseContentTypeEquals( + "text/css; charset=UTF-8", // <- expected + "text/css", INIT_PARAM_VALUE_WHEN_TEMPLATE_HAS_MIME_TYPE, // <- init-params + "foo.ftl", null); // <- request + assertResponseContentTypeEquals( + "application/json", // <- expected + null, INIT_PARAM_VALUE_WHEN_TEMPLATE_HAS_MIME_TYPE, // <- init-params + "foo.ftl", "application/json"); // <- request + assertResponseContentTypeEquals( + "application/json", // <- expected + "text/css", INIT_PARAM_VALUE_WHEN_TEMPLATE_HAS_MIME_TYPE, // <- init-params + "foo.ftl", "application/json"); // <- request + assertResponseContentTypeEquals( + "text/plain", // <- expected + null, INIT_PARAM_VALUE_WHEN_TEMPLATE_HAS_MIME_TYPE, // <- init-params + "contentTypeAttr.ftl", "application/json"); // <- request + assertResponseContentTypeEquals( + "text/plain; charset=UTF-8", // <- expected + null, INIT_PARAM_VALUE_WHEN_TEMPLATE_HAS_MIME_TYPE, // <- init-params + "outputFormatHeader.ftl", "application/json"); // <- request + + assertResponseContentTypeEquals( + DEFAULT_CONTENT_TYPE + "; charset=UTF-8", // <- expected + null, INIT_PARAM_VALUE_NEVER, // <- init-params + "foo.ftl", null); // <- request + assertResponseContentTypeEquals( + "text/css; charset=UTF-8", // <- expected + "text/css", INIT_PARAM_VALUE_NEVER, // <- init-params + "foo.ftl", null); // <- request + assertResponseContentTypeEquals( + "application/json", // <- expected + null, INIT_PARAM_VALUE_NEVER, // <- init-params + "foo.ftl", "application/json"); // <- request + assertResponseContentTypeEquals( + "application/json", // <- expected + "text/css", INIT_PARAM_VALUE_NEVER, // <- init-params + "foo.ftl", "application/json"); // <- request + assertResponseContentTypeEquals( + "application/json", // <- expected + null, INIT_PARAM_VALUE_NEVER, // <- init-params + "contentTypeAttr.ftl", "application/json"); // <- request + assertResponseContentTypeEquals( + "application/json", // <- expected + null, INIT_PARAM_VALUE_NEVER, // <- init-params + "outputFormatHeader.ftl", "application/json"); // <- request } - @Test - public void testContentTypeInitParams_outputFormatAlwaysWins_NoOverriding() throws ServletException, IOException { - MockHttpServletRequest request = createMockHttpServletRequest(servletContext, "/outputFormatHeader.ftl"); + private void assertResponseContentTypeEquals( + String exptectContentType, + String ctInitParam, String overrideCTInitParam, + String templateName, String responseCT) + throws ServletException, IOException { + MockHttpServletRequest request = createMockHttpServletRequest(servletContext, templateName); + MockHttpServletResponse response = new MockHttpServletResponse(); - response.setContentType("application/json"); - assertEquals("application/json", response.getContentType()); - - createFreemarkerServlet(FreemarkerServlet.INIT_PARAM_OVERRIDE_RESPONSE_CONTENT_TYPE, "false") - .doGet(request, response); - LOG.debug("response content: " + response.getContentAsString()); - - assertEquals(HttpServletResponse.SC_OK, response.getStatus()); - assertEquals("text/plain", response.getContentType()); - } + if (responseCT != null) { + response.setContentType(responseCT); + assertEquals(responseCT, response.getContentType()); + } else { + assertNull(response.getContentType()); + } - private FreemarkerServlet createFreemarkerServlet(String... initParams) throws ServletException { MockServletConfig servletConfig = new MockServletConfig(servletContext); - servletConfig.addInitParameter(FreemarkerServlet.INIT_PARAM_TEMPLATE_PATH, TEST_TEMPLATE_PATH); - Assert.isTrue(initParams.length % 2 == 0); - for (int i = 0; i < initParams.length; i += 2) { - servletConfig.addInitParameter(initParams[i], initParams[i + 1]); + servletConfig.addInitParameter(INIT_PARAM_TEMPLATE_PATH, TEST_TEMPLATE_PATH); + servletConfig.addInitParameter(Configuration.DEFAULT_ENCODING_KEY, "UTF-8"); + if (ctInitParam != null) { + servletConfig.addInitParameter(INIT_PARAM_CONTENT_TYPE, ctInitParam); + } + if (overrideCTInitParam != null) { + servletConfig.addInitParameter(INIT_PARAM_OVERRIDE_RESPONSE_CONTENT_TYPE, overrideCTInitParam); } FreemarkerServlet freemarkerServlet = new FreemarkerServlet(); - freemarkerServlet.init(servletConfig); - return freemarkerServlet; + try { + freemarkerServlet.init(servletConfig); + + freemarkerServlet.doGet(request, response); + + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + assertEquals(exptectContentType, response.getContentType()); + } finally { + freemarkerServlet.destroy(); + } } private MockHttpServletRequest createMockHttpServletRequest(final ServletContext servletContext,
