freemarker-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ddek...@apache.org
Subject [2/2] incubator-freemarker git commit: Added new init-param, ResponseCharacterEncoding: Deprecates the old (and quirky) way of specifying the output charset, which was specifying it in the ContentType, or falling back to using the template file charset.
Date Sun, 15 Nov 2015 20:53:33 GMT
Added new init-param, ResponseCharacterEncoding: Deprecates the old (and quirky) way of specifying the output charset, which was specifying it in the ContentType, or falling back to using the template file charset. The possible value are legacy (the default for backward compatibility), fromTemplate (which is legacy without quirks), doNotSet (keeps what the caller has already set in the ServletRespone) and force charsetName (forces a specific charset).

Along the way, also made FreemarkerServletTest-s easier to write.


Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/f64aa1f0
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/f64aa1f0
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/f64aa1f0

Branch: refs/heads/2.3-gae
Commit: f64aa1f0d6f071e858b2ff41a325dca722735579
Parents: e96460c
Author: ddekany <ddekany@apache.org>
Authored: Sun Nov 15 21:53:19 2015 +0100
Committer: ddekany <ddekany@apache.org>
Committed: Sun Nov 15 21:53:19 2015 +0100

----------------------------------------------------------------------
 .../ext/servlet/FreemarkerServlet.java          | 114 +++++-
 src/manual/book.xml                             |  20 +-
 .../ext/servlet/FreemarkerServletTest.java      | 396 +++++++++++++++----
 .../freemarker/ext/servlet/contentTypeAttr.ftl  |  20 -
 .../resources/freemarker/ext/servlet/foo.ftl    |  19 -
 .../ext/servlet/outputFormatHeader.ftl          |  20 -
 6 files changed, 434 insertions(+), 155 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f64aa1f0/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 464be23..695ac8d 100644
--- a/src/main/java/freemarker/ext/servlet/FreemarkerServlet.java
+++ b/src/main/java/freemarker/ext/servlet/FreemarkerServlet.java
@@ -20,6 +20,7 @@
 package freemarker.ext.servlet;
 
 import java.io.IOException;
+import java.nio.charset.Charset;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
@@ -45,6 +46,7 @@ import freemarker.cache.MultiTemplateLoader;
 import freemarker.cache.TemplateLoader;
 import freemarker.cache.WebappTemplateLoader;
 import freemarker.core.Configurable;
+import freemarker.core.Environment;
 import freemarker.core.OutputFormat;
 import freemarker.core.UndefinedOutputFormat;
 import freemarker.ext.jsp.TaglibFactory;
@@ -143,9 +145,8 @@ import freemarker.template.utility.StringUtil;
  * <li>If the {@value #INIT_PARAM_OVERRIDE_RESPONSE_CONTENT_TYPE} init-param is {@value #INIT_PARAM_VALUE_NEVER} (the
  * default is {@value #INIT_PARAM_VALUE_ALWAYS}), then the value of {@link HttpServletResponse#getContentType()} is used
  * if that's non-{@code null}.
- * <li>The template's custom attribute name <tt>content_type</tt>, usually specified via the <tt>attributes</tt>
- * parameter of the <tt>&lt;#ftl&gt;</tt> directive. This is a legacy feature, deprecated by the {@link OutputFormat}
- * mechanism.
+ * <li>The template's <tt>content_type</tt> custom attribute, usually specified via the <tt>attributes</tt> parameter of
+ * the <tt>&lt;#ftl&gt;</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#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}
@@ -156,10 +157,13 @@ import freemarker.template.utility.StringUtil;
  * (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 be 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>. If and only if
+ * the {@value #INIT_PARAM_RESPONSE_CHARACTER_ENCODING} init-param is set to {@value #INIT_PARAM_VALUE_LEGACY} (which is
+ * the default of it), the content type may include the charset (as in <tt>"text/html; charset=utf-8"</tt>), in which
+ * case that specifies the actual charset of the output. If the the {@value #INIT_PARAM_RESPONSE_CHARACTER_ENCODING}
+ * init-param is not set to {@value #INIT_PARAM_VALUE_LEGACY}, then specifying the charset in the content type is not
+ * allowed, and will cause servlet initialization error.
+ * </li>
  *
  * <li><strong>{@value #INIT_PARAM_OVERRIDE_RESPONSE_CONTENT_TYPE}</strong> (since 2.3.24): Specifies when we should
  * override the {@code contentType} that might be already set (i.e., non-{@code null}) in the
@@ -178,6 +182,38 @@ import freemarker.template.utility.StringUtil;
  * invoking {@link #deduceLocale(String, HttpServletRequest, HttpServletResponse)}. Another possible value is
  * {@value #INIT_PARAM_VALUE_NEVER}, which means that we don't deduce the template {@code locale}, unless
  * {@link HttpServletRequest#getLocale()} is {@code null}.
+ * 
+ * <li><strong>{@value #INIT_PARAM_RESPONSE_CHARACTER_ENCODING}</strong> (since 2.3.24): Specifies how the
+ * {@link HttpServletResponse} "character encoding" (as in {@link HttpServletResponse#setCharacterEncoding(String)})
+ * will deduced. The possible modes are:
+ * <ul>
+ * <li>{@value #INIT_PARAM_VALUE_LEGACY}: This is the default for backward compatibility; in new applications, use
+ * {@value #INIT_PARAM_VALUE_FROM_TEMPLATE} (or some of the other options) instead. {@value #INIT_PARAM_VALUE_LEGACY}
+ * will use the charset of the template file to set the charset of the servlet response. Except, if the
+ * {@value #INIT_PARAM_CONTENT_TYPE} init-param contains a charset, it will use that instead. A quirk of this legacy
+ * mode is that it's not aware of the {@link Configurable#getOutputEncoding()} FreeMarker setting, and thus never reads
+ * or writes it (though very few applications utilize that setting anyway). Also, it sets the charset of the servlet
+ * response by adding it to the response content type via calling {@link HttpServletResponse#setContentType(String)} (as
+ * that was the only way before Servlet 2.4), not via the more modern
+ * {@link HttpServletResponse#setCharacterEncoding(String)} method. Note that the charset of a template almost always
+ * comes from {@link Configuration#getDefaultEncoding()} (i.e., from the {@code default_encoding} FreeMarker setting),
+ * occasionally from {@link Configuration#getEncoding(Locale)} (when FreeMarker was configured to use different charsets
+ * depending on the locale) or even more rarely from {@link Configuration#getTemplateConfigurers()} (when FreeMarker was
+ * configured to use a specific charset for certain templates).
+ * <li>{@value #INIT_PARAM_VALUE_FROM_TEMPLATE}: This should be used in most applications, but it's not the default for
+ * backward compatibility. It reads the {@link Configurable#getOutputEncoding()} setting of the template (note that the
+ * template usually just inherits that from the {@link Configuration}), and if that's not set, then reads the source
+ * charset of the template, just like {@value #INIT_PARAM_VALUE_LEGACY}. Then it passes the charset acquired this way to
+ * {@link HttpServletResponse#setCharacterEncoding(String)} and {@link Environment#setOutputEncoding(String)}. (It
+ * doesn't call the legacy {@link HttpServletResponse#setContentType(String)} API to set the charset.)
+ * <li>{@value #INIT_PARAM_VALUE_DO_NOT_SET}: {@link FreemarkerServlet} will not set the {@link HttpServletResponse}
+ * "character encoding". It will still call {@link Environment#setOutputEncoding(String)}, so that the running template
+ * will be aware of the charset used for the output.
+ * <li>{@value #INIT_PARAM_VALUE_FORCE_PREFIX} + charset name, for example {@code force UTF-8}: The output charset will
+ * be the one specified after "force" + space, regardless of everything. The charset specified this way is passed to
+ * {@link HttpServletResponse#setCharacterEncoding(String)} and {@link Environment#setOutputEncoding(String)}. If the
+ * charset name is not recognized by Java, the servlet initialization will fail.
+ * </ul>
  *
  * <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.
@@ -379,6 +415,7 @@ public class FreemarkerServlet extends HttpServlet {
     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";
+    public static final String INIT_PARAM_VALUE_FORCE_PREFIX = "force ";
 
     /**
      * When set, the items defined in it will be added after those coming from the
@@ -478,6 +515,7 @@ public class FreemarkerServlet extends HttpServlet {
     private OverrideResponseContentType overrideResponseContentType = initParamValueToEnum(
             getDefaultOverrideResponseContentType(), OverrideResponseContentType.values());
     private ResponseCharacterEncoding responseCharacterEncoding = ResponseCharacterEncoding.LEGACY;
+    private Charset forcedResponseCharacterEncoding;
     private boolean contentTypeContainsCharset;
     private OverrideResponseLocale overrideResponseLocale = OverrideResponseLocale.ALWAYS;
     private List/*<MetaInfTldSource>*/ metaInfTldSources;
@@ -628,6 +666,10 @@ public class FreemarkerServlet extends HttpServlet {
                     overrideResponseContentType = initParamValueToEnum(value, OverrideResponseContentType.values());
                 } else if (name.equals(INIT_PARAM_RESPONSE_CHARACTER_ENCODING)) {
                     responseCharacterEncoding = initParamValueToEnum(value, ResponseCharacterEncoding.values());
+                    if (responseCharacterEncoding == ResponseCharacterEncoding.FORCE_CHARSET) {
+                        String charsetName = value.substring(INIT_PARAM_VALUE_FORCE_PREFIX.length()).trim();
+                        forcedResponseCharacterEncoding = Charset.forName(charsetName);
+                    }
                 } else if (name.equals(INIT_PARAM_OVERRIDE_RESPONSE_LOCALE)) {
                     overrideResponseLocale = initParamValueToEnum(value, OverrideResponseLocale.values());
                 } else if (name.equals(INIT_PARAM_EXCEPTION_ON_MISSING_TEMPLATE)) {
@@ -652,6 +694,12 @@ public class FreemarkerServlet extends HttpServlet {
         } // while initpnames
         
         contentTypeContainsCharset = contentTypeContainsCharset(contentType);
+        if (contentTypeContainsCharset && responseCharacterEncoding != ResponseCharacterEncoding.LEGACY) {
+            throw new InitParamValueException(INIT_PARAM_CONTENT_TYPE, contentType,
+                    new IllegalStateException("You can't specify the charset in the content type, because the \"" +
+                            INIT_PARAM_RESPONSE_CHARACTER_ENCODING + "\" init-param isn't set to "
+                            + "\"" + INIT_PARAM_VALUE_LEGACY + "\"."));
+        }
     }
     
     private boolean contentTypeContainsCharset(String contentType) {
@@ -806,8 +854,11 @@ public class FreemarkerServlet extends HttpServlet {
         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}"
+            if (responseCharacterEncoding != ResponseCharacterEncoding.FORCE_CHARSET) {
+                response.setCharacterEncoding(getTemplateSpecificOutputEncoding(template));
+            } else {
+                response.setCharacterEncoding(forcedResponseCharacterEncoding.name());
+            }
         }
 
         setBrowserCachingPolicy(response);
@@ -822,7 +873,14 @@ public class FreemarkerServlet extends HttpServlet {
             if (preTemplateProcess(request, response, template, model)) {
                 try {
                     // Process the template
-                    template.process(model, response.getWriter());
+                    Environment env = template.createProcessingEnvironment(model, response.getWriter());
+                    if (responseCharacterEncoding != ResponseCharacterEncoding.LEGACY) {
+                        String actualOutputCharset = response.getCharacterEncoding();
+                        if (actualOutputCharset != null) {
+                            env.setOutputEncoding(actualOutputCharset);
+                        }
+                    }
+                    processEnvironment(env, request, response);
                 } finally {
                     // Give subclasses a chance to hook into postprocessing
                     postTemplateProcess(request, response, template, model);
@@ -839,6 +897,23 @@ public class FreemarkerServlet extends HttpServlet {
         }
     }
 
+    /**
+     * This is the method that actually executes the template. The original implementation coming from
+     * {@link FreemarkerServlet} simply calls {@link Environment#process()}. Overriding this method allows you to
+     * prepare the {@link Environment} before the execution, or extract information from the {@link Environment} after
+     * the execution. It also allows you to capture exceptions throw by the template.
+     * 
+     * @param env
+     *            The {@link Environment} object already set up to execute the template. You only have to call
+     *            {@link Environment#process()} and the output will be produced by the template.
+     * 
+     * @since 2.3.24
+     */
+    protected void processEnvironment(Environment env, HttpServletRequest request, HttpServletResponse response)
+            throws TemplateException, IOException {
+        env.process();
+    }
+
     private String getTemplateSpecificOutputEncoding(Template template) {
         String outputEncoding = responseCharacterEncoding == ResponseCharacterEncoding.LEGACY ? null
                 : template.getOutputEncoding();
@@ -856,7 +931,7 @@ public class FreemarkerServlet extends HttpServlet {
         String outputFormatMimeType = template.getOutputFormat().getMimeType();
         if (outputFormatMimeType != null) {
             if (responseCharacterEncoding == ResponseCharacterEncoding.LEGACY) {
-                // In legacy mode we won't call serlvetResponse.getCharacterEncoding(...), so:
+                // In legacy mode we won't call serlvetResponse.setCharacterEncoding(...), so:
                 outputFormatMimeType += "; charset=" + getTemplateSpecificOutputEncoding(template);
             }
             return outputFormatMimeType; 
@@ -1479,10 +1554,13 @@ 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;
+    private <T extends InitParamValueEnum> T initParamValueToEnum(String initParamValue, T[] enumValues) {
+        for (T enumValue : enumValues) {
+            String enumInitParamValue = enumValue.getInitParamValue();
+            if (initParamValue.equals(enumInitParamValue)
+                    || enumInitParamValue.endsWith("}") && initParamValue.startsWith(
+                            enumInitParamValue.substring(0, enumInitParamValue.indexOf("${")))) {
+                return enumValue;
             }
         }
         
@@ -1490,7 +1568,7 @@ public class FreemarkerServlet extends HttpServlet {
         sb.append(StringUtil.jQuote(initParamValue));
         sb.append(" is not a one of the enumeration values: ");
         boolean first = true;
-        for (T value : values) {
+        for (T value : enumValues) {
             if (!first) {
                 sb.append(", ");
             } else {
@@ -1530,8 +1608,8 @@ public class FreemarkerServlet extends HttpServlet {
     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}"
+        DO_NOT_SET(INIT_PARAM_VALUE_DO_NOT_SET),
+        FORCE_CHARSET(INIT_PARAM_VALUE_FORCE_PREFIX + "${charsetName}");
 
         private final String initParamValue;
         

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f64aa1f0/src/manual/book.xml
----------------------------------------------------------------------
diff --git a/src/manual/book.xml b/src/manual/book.xml
index cab5f2c..bf04cd9 100644
--- a/src/manual/book.xml
+++ b/src/manual/book.xml
@@ -26046,7 +26046,7 @@ TemplateModel x = env.getVariable("x");  // get variable x</programlisting>
 
               <itemizedlist>
                 <listitem>
-                  <para><literal>OverrideResponseContentType</literal>.
+                  <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
@@ -26059,13 +26059,29 @@ TemplateModel x = env.getVariable("x");  // get variable x</programlisting>
                 </listitem>
 
                 <listitem>
-                  <para><literal>OverrideResponseLocale</literal>. Specifies
+                  <para><literal>OverrideResponseLocale</literal>: Specifies
                   if 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, but now this behavior can be changed so that
                   we only set it if it wasn't already set.</para>
                 </listitem>
+
+                <listitem>
+                  <para><literal>ResponseCharacterEncoding</literal>:
+                  Deprecates the old (and quirky) way of specifying the output
+                  charset, which was specifying it in the
+                  <literal>ContentType</literal>, or falling back to using the
+                  template file charset. The possible value are
+                  <literal>legacy</literal> (the default for backward
+                  compatibility), <literal>fromTemplate</literal> (which is
+                  <literal>legacy</literal> without quirks),
+                  <literal>doNotSet</literal> (keeps what the caller has
+                  already set in the <literal>ServletRespone</literal>) and
+                  <literal>force
+                  <replaceable>charsetName</replaceable></literal> (forces a
+                  specific charset).</para>
+                </listitem>
               </itemizedlist>
             </listitem>
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f64aa1f0/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 fc81de9..b95cda5 100644
--- a/src/test/java/freemarker/ext/servlet/FreemarkerServletTest.java
+++ b/src/test/java/freemarker/ext/servlet/FreemarkerServletTest.java
@@ -19,9 +19,11 @@
 package freemarker.ext.servlet;
 
 import static freemarker.ext.servlet.FreemarkerServlet.*;
+import static org.hamcrest.Matchers.*;
 import static org.junit.Assert.*;
 
 import java.io.IOException;
+import java.nio.charset.UnsupportedCharsetException;
 import java.util.Locale;
 
 import javax.servlet.ServletContext;
@@ -36,13 +38,29 @@ import org.springframework.mock.web.MockHttpServletResponse;
 import org.springframework.mock.web.MockServletConfig;
 import org.springframework.mock.web.MockServletContext;
 
+import freemarker.cache.ConditionalTemplateConfigurerFactory;
+import freemarker.cache.FileNameGlobMatcher;
+import freemarker.cache.FirstMatchTemplateConfigurerFactory;
+import freemarker.cache.StringTemplateLoader;
+import freemarker.cache.TemplateLoader;
+import freemarker.core.Environment;
+import freemarker.core.TemplateConfigurer;
 import freemarker.template.Configuration;
 import freemarker.template.Template;
-import freemarker.template.TemplateModel;
+import freemarker.template.TemplateException;
 
 public class FreemarkerServletTest {
 
-    private static final String TEST_TEMPLATE_PATH = "classpath:freemarker/ext/servlet";
+    private static final String OUTPUT_FORMAT_HEADER_FTL = "outputFormatHeader.ftl";
+    private static final String CONTENT_TYPE_ATTR_FTL = "contentTypeAttr.ftl";
+    private static final String FOO_FTL = "foo.ftl";
+    private static final String FOO_SRC_UTF8_FTL = "foo-src-utf8.ftl";
+    private static final String FOO_OUT_UTF8_FTL = "foo-out-utf8.ftl";
+
+    private static final Locale DEFAULT_LOCALE = Locale.US;
+    private static final String CFG_DEFAULT_ENCODING = "US-ASCII";
+    /** According to the Servlet Specification */
+    private static final String SERVLET_RESPONSE_DEFAULT_CHARSET = "ISO-8859-1";
 
     private MockServletContext servletContext;
 
@@ -59,115 +77,245 @@ public class FreemarkerServletTest {
             assertResponseContentTypeEquals(
                     DEFAULT_CONTENT_TYPE + "; charset=UTF-8", // <- expected
                     null, overrideCT, // <- init-params
-                    "foo.ftl", null); // <- request
+                    FOO_FTL, null); // <- request
             assertResponseContentTypeEquals(
                     "text/css; charset=UTF-8", // <- expected
                     "text/css", overrideCT, // <- init-params
-                    "foo.ftl", null); // <- request
+                    FOO_FTL, null); // <- request
             assertResponseContentTypeEquals(
                     DEFAULT_CONTENT_TYPE + "; charset=UTF-8", // <- expected
                     null, overrideCT, // <- init-params
-                    "foo.ftl", "application/json"); // <- request
+                    FOO_FTL, "application/json"); // <- request
             assertResponseContentTypeEquals(
                     "text/css; charset=UTF-8", // <- expected
                     "text/css", overrideCT, // <- init-params
-                    "foo.ftl", "application/json"); // <- request
+                    FOO_FTL, "application/json"); // <- request
             assertResponseContentTypeEquals(
                     "text/plain", // <- expected
                     null, overrideCT, // <- init-params
-                    "contentTypeAttr.ftl", "application/json"); // <- request
+                    CONTENT_TYPE_ATTR_FTL, "application/json"); // <- request
             assertResponseContentTypeEquals(
                     "text/plain; charset=UTF-8", // <- expected
                     null, overrideCT, // <- init-params
-                    "outputFormatHeader.ftl", "application/json"); // <- request
+                    OUTPUT_FORMAT_HEADER_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
+                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        
+                FOO_FTL, null); // <- request
         assertResponseContentTypeEquals(
                 "application/json", // <- expected
                 null, INIT_PARAM_VALUE_WHEN_TEMPLATE_HAS_MIME_TYPE, // <- init-params
-                "foo.ftl", "application/json"); // <- request
+                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
+                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
+                CONTENT_TYPE_ATTR_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
-        
+                OUTPUT_FORMAT_HEADER_FTL, "application/json"); // <- request
+
         assertResponseContentTypeEquals(
                 DEFAULT_CONTENT_TYPE + "; charset=UTF-8", // <- expected
                 null, INIT_PARAM_VALUE_NEVER, // <- init-params
-                "foo.ftl", null); // <- request
+                FOO_FTL, null); // <- request
         assertResponseContentTypeEquals(
                 "text/css; charset=UTF-8", // <- expected
                 "text/css", INIT_PARAM_VALUE_NEVER, // <- init-params
-                "foo.ftl", null); // <- request        
+                FOO_FTL, null); // <- request
         assertResponseContentTypeEquals(
                 "application/json", // <- expected
                 null, INIT_PARAM_VALUE_NEVER, // <- init-params
-                "foo.ftl", "application/json"); // <- request
+                FOO_FTL, "application/json"); // <- request
         assertResponseContentTypeEquals(
                 "application/json", // <- expected
                 "text/css", INIT_PARAM_VALUE_NEVER, // <- init-params
-                "foo.ftl", "application/json"); // <- request
+                FOO_FTL, "application/json"); // <- request
         assertResponseContentTypeEquals(
                 "application/json", // <- expected
                 null, INIT_PARAM_VALUE_NEVER, // <- init-params
-                "contentTypeAttr.ftl", "application/json"); // <- request
+                CONTENT_TYPE_ATTR_FTL, "application/json"); // <- request
         assertResponseContentTypeEquals(
                 "application/json", // <- expected
                 null, INIT_PARAM_VALUE_NEVER, // <- init-params
-                "outputFormatHeader.ftl", "application/json"); // <- request
+                OUTPUT_FORMAT_HEADER_FTL, "application/json"); // <- request
     }
 
     @Test
     public void testResponseLocaleInitParams() throws Exception {
-        Locale prevDefaultLocale = Locale.getDefault();
-        Locale.setDefault(Locale.US);
+        assertTemplateLocaleEquals(
+                DEFAULT_LOCALE, // <- expected template locale
+                null, // <- request locale
+                null, // <- init-param
+                FOO_FTL);
+        assertTemplateLocaleEquals(
+                DEFAULT_LOCALE, // <- expected template locale
+                Locale.FRENCH, // <- request locale
+                null, // <- init-param
+                FOO_FTL);
+        assertTemplateLocaleEquals(
+                DEFAULT_LOCALE, // <- expected template locale
+                Locale.FRENCH, // <- request locale
+                INIT_PARAM_VALUE_ALWAYS, // <- init-param
+                FOO_FTL);
+        assertTemplateLocaleEquals(
+                DEFAULT_LOCALE, // <- expected template locale
+                null, // <- request locale
+                INIT_PARAM_VALUE_NEVER, // <- init-param
+                FOO_FTL);
+        assertTemplateLocaleEquals(
+                Locale.FRENCH, // <- expected template locale
+                Locale.FRENCH, // <- request locale
+                INIT_PARAM_VALUE_NEVER, // <- init-param
+                FOO_FTL);
+    }
+
+    @Test
+    public void testResponseOutputCharsetInitParam() throws Exception {
+        // Legacy mode is not aware of the outputEncoding, thus it doesn't set it:
+        assertOutputEncodingEquals(
+                CFG_DEFAULT_ENCODING, // <- expected response.characterEncoding
+                null, // <- expected env.outputEncoding
+                null, // <- init-param
+                FOO_FTL);
+        assertOutputEncodingEquals(
+                CFG_DEFAULT_ENCODING, // <- expected response.characterEncoding
+                null, // <- expected env.outputEncoding
+                FreemarkerServlet.INIT_PARAM_VALUE_LEGACY, // <- init-param
+                FOO_FTL);
+        // Legacy mode follows the source encoding of the template:
+        assertOutputEncodingEquals(
+                "UTF-8", // <- expected response.characterEncoding
+                null, // <- expected env.outputEncoding
+                null, // <- init-param
+                FOO_SRC_UTF8_FTL);
+        // Legacy mode doesn't deal with outputEncoding, but it's inherited by the Environment from the Template:
+        assertOutputEncodingEquals(
+                CFG_DEFAULT_ENCODING, // <- expected response.characterEncoding
+                "UTF-8", // <- expected env.outputEncoding
+                null, // <- init-param
+                FOO_OUT_UTF8_FTL);
+        // Charset in content type is the strongest:
+        assertOutputEncodingEquals(
+                "ISO-8859-2", // <- expected response.characterEncoding
+                null, // <- expected env.outputEncoding
+                null, // <- init-param
+                "text/html; charset=ISO-8859-2", // ContentType init-param
+                FOO_FTL);
+        assertOutputEncodingEquals(
+                "ISO-8859-2", // <- expected response.characterEncoding
+                null, // <- expected env.outputEncoding
+                null, // <- init-param
+                "text/html; charset=ISO-8859-2", // ContentType init-param
+                FOO_SRC_UTF8_FTL);
+        
+        // Non-legacy mode always keeps env.outputEncoding in sync. with the Servlet response encoding:
+        assertOutputEncodingEquals(
+                CFG_DEFAULT_ENCODING, // <- expected response.characterEncoding
+                CFG_DEFAULT_ENCODING, // <- expected env.outputEncoding
+                FreemarkerServlet.INIT_PARAM_VALUE_FROM_TEMPLATE, // <- init-param
+                FOO_FTL);
+        // Non-legacy mode considers the template-specific outputEncoding:
+        assertOutputEncodingEquals(
+                "UTF-8", // <- expected response.characterEncoding
+                "UTF-8", // <- expected env.outputEncoding
+                FreemarkerServlet.INIT_PARAM_VALUE_FROM_TEMPLATE, // <- init-param
+                FOO_OUT_UTF8_FTL);
+        // Non-legacy mode uses the template source encoding as a fallback for outputEncoding:
+        assertOutputEncodingEquals(
+                "UTF-8", // <- expected response.characterEncoding
+                "UTF-8", // <- expected env.outputEncoding
+                FreemarkerServlet.INIT_PARAM_VALUE_FROM_TEMPLATE, // <- init-param
+                FOO_SRC_UTF8_FTL);
+        // Not allowed to specify the charset in the contentType init-param: 
         try {
-            // By default, the Configurable.locale is set to Locale.getDefault().
-            final Locale defaultLocale = Locale.getDefault();
-    
-            assertTemplateLocaleEquals(
-                    defaultLocale, // <- expected template locale
-                    null, // <- request locale
-                    null, // <- init-param
-                    "foo.ftl");
-            assertTemplateLocaleEquals(
-                    defaultLocale, // <- expected template locale
-                    Locale.FRENCH, // <- request locale
-                    null, // <- init-param
-                    "foo.ftl");
-            assertTemplateLocaleEquals(
-                    defaultLocale, // <- expected template locale
-                    Locale.FRENCH, // <- request locale
-                    INIT_PARAM_VALUE_ALWAYS, // <- init-param
-                    "foo.ftl");
-            assertTemplateLocaleEquals(
-                    defaultLocale, // <- expected template locale
-                    null, // <- request locale
-                    INIT_PARAM_VALUE_NEVER, // <- init-param
-                    "foo.ftl");
-            assertTemplateLocaleEquals(
-                    Locale.FRENCH, // <- expected template locale
-                    Locale.FRENCH, // <- request locale
-                    INIT_PARAM_VALUE_NEVER, // <- init-param
-                    "foo.ftl");
-        } finally {
-            Locale.setDefault(prevDefaultLocale);
+            assertOutputEncodingEquals(
+                    null, // <- expected response.characterEncoding
+                    null, // <- expected env.outputEncoding
+                    FreemarkerServlet.INIT_PARAM_VALUE_FROM_TEMPLATE, // <- init-param
+                    "text/html; charset=ISO-8859-2", // ContentType init-param
+                    FOO_FTL);
+            fail();
+        } catch (ServletException e) {
+            assertThat(e.getCause().getCause().getMessage(), containsString(FreemarkerServlet.INIT_PARAM_VALUE_LEGACY));
+        }
+        
+        // Do not set mode:
+        assertOutputEncodingEquals(
+                SERVLET_RESPONSE_DEFAULT_CHARSET, // <- expected response.characterEncoding
+                SERVLET_RESPONSE_DEFAULT_CHARSET, // <- expected env.outputEncoding
+                FreemarkerServlet.INIT_PARAM_VALUE_DO_NOT_SET, // <- init-param
+                FOO_FTL);
+        assertOutputEncodingEquals(
+                SERVLET_RESPONSE_DEFAULT_CHARSET, // <- expected response.characterEncoding
+                SERVLET_RESPONSE_DEFAULT_CHARSET, // <- expected env.outputEncoding
+                FreemarkerServlet.INIT_PARAM_VALUE_DO_NOT_SET, // <- init-param
+                FOO_SRC_UTF8_FTL);
+        assertOutputEncodingEquals(
+                SERVLET_RESPONSE_DEFAULT_CHARSET, // <- expected response.characterEncoding
+                SERVLET_RESPONSE_DEFAULT_CHARSET, // <- expected env.outputEncoding
+                FreemarkerServlet.INIT_PARAM_VALUE_DO_NOT_SET, // <- init-param
+                FOO_OUT_UTF8_FTL);
+        // Not allowed to specify the charset in the contentType init-param: 
+        try {
+            assertOutputEncodingEquals(
+                    null, // <- expected response.characterEncoding
+                    null, // <- expected env.outputEncoding
+                    FreemarkerServlet.INIT_PARAM_VALUE_DO_NOT_SET, // <- init-param
+                    "text/html; charset=ISO-8859-2", // ContentType init-param
+                    FOO_FTL);
+            fail();
+        } catch (ServletException e) {
+            assertThat(e.getCause().getCause().getMessage(), containsString(FreemarkerServlet.INIT_PARAM_VALUE_LEGACY));
+        }
+        
+        // Forced mode:
+        assertOutputEncodingEquals(
+                "UTF-16LE", // <- expected response.characterEncoding
+                "UTF-16LE", // <- expected env.outputEncoding
+                FreemarkerServlet.INIT_PARAM_VALUE_FORCE_PREFIX + "UTF-16LE", // <- init-param
+                FOO_FTL);
+        assertOutputEncodingEquals(
+                "UTF-16LE", // <- expected response.characterEncoding
+                "UTF-16LE", // <- expected env.outputEncoding
+                FreemarkerServlet.INIT_PARAM_VALUE_FORCE_PREFIX + "UTF-16LE", // <- init-param
+                FOO_SRC_UTF8_FTL);
+        assertOutputEncodingEquals(
+                "UTF-16LE", // <- expected response.characterEncoding
+                "UTF-16LE", // <- expected env.outputEncoding
+                FreemarkerServlet.INIT_PARAM_VALUE_FORCE_PREFIX + "UTF-16LE", // <- init-param
+                FOO_OUT_UTF8_FTL);
+        try {
+            assertOutputEncodingEquals(
+                    null, // <- expected response.characterEncoding
+                    null, // <- expected env.outputEncoding
+                    FreemarkerServlet.INIT_PARAM_VALUE_FORCE_PREFIX + "noSuchCharset", // <- init-param
+                    FOO_FTL);
+            fail();
+        } catch (ServletException e) {
+            assertThat(e.getCause().getCause(), instanceOf(UnsupportedCharsetException.class));
+        }
+        // Not allowed to specify the charset in the contentType init-param: 
+        try {
+            assertOutputEncodingEquals(
+                    null, // <- expected response.characterEncoding
+                    null, // <- expected env.outputEncoding
+                    FreemarkerServlet.INIT_PARAM_VALUE_FORCE_PREFIX + "UTF-16LE", // <- init-param
+                    "text/html; charset=ISO-8859-2", // ContentType init-param
+                    FOO_FTL);
+            fail();
+        } catch (ServletException e) {
+            assertThat(e.getCause().getCause().getMessage(), containsString(FreemarkerServlet.INIT_PARAM_VALUE_LEGACY));
         }
     }
 
@@ -177,7 +325,7 @@ public class FreemarkerServletTest {
             String templateName, String responseCT)
                     throws ServletException, IOException {
         MockHttpServletRequest request = createMockHttpServletRequest(servletContext, templateName, null);
-        
+
         MockHttpServletResponse response = new MockHttpServletResponse();
         if (responseCT != null) {
             response.setContentType(responseCT);
@@ -185,23 +333,22 @@ public class FreemarkerServletTest {
         } else {
             assertNull(response.getContentType());
         }
-    
+
         MockServletConfig servletConfig = new MockServletConfig(servletContext);
-        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);            
+            servletConfig.addInitParameter(INIT_PARAM_CONTENT_TYPE, ctInitParam);
         }
         if (overrideCTInitParam != null) {
             servletConfig.addInitParameter(INIT_PARAM_OVERRIDE_RESPONSE_CONTENT_TYPE, overrideCTInitParam);
         }
-        
-        FreemarkerServlet freemarkerServlet = new FreemarkerServlet();
+
+        TestFreemarkerServlet freemarkerServlet = new TestFreemarkerServlet();
         try {
             freemarkerServlet.init(servletConfig);
-            
+
             freemarkerServlet.doGet(request, response);
-        
+
             assertEquals(HttpServletResponse.SC_OK, response.getStatus());
             assertEquals(exptectContentType, response.getContentType());
         } finally {
@@ -219,33 +366,65 @@ public class FreemarkerServletTest {
         MockHttpServletResponse response = new MockHttpServletResponse();
 
         MockServletConfig servletConfig = new MockServletConfig(servletContext);
-        servletConfig.addInitParameter(INIT_PARAM_TEMPLATE_PATH, TEST_TEMPLATE_PATH);
 
         if (overrideResponseLocaleInitParam != null) {
             servletConfig.addInitParameter(INIT_PARAM_OVERRIDE_RESPONSE_LOCALE, overrideResponseLocaleInitParam);
         }
 
-        final Template [] processedTemplateHolder = new Template[1];
+        TestFreemarkerServlet freemarkerServlet = new TestFreemarkerServlet();
 
-        FreemarkerServlet freemarkerServlet = new FreemarkerServlet() {
+        try {
+            freemarkerServlet.init(servletConfig);
+            freemarkerServlet.doGet(request, response);
 
-            @Override
-            protected void postTemplateProcess(
-                    HttpServletRequest request,
-                    HttpServletResponse response,
-                    Template template,
-                    TemplateModel data)
-                            throws ServletException, IOException {
-                processedTemplateHolder[0] = template;
-            }
-        };
+            assertEquals(HttpServletResponse.SC_OK, response.getStatus());
+            assertEquals(exptectLocale, freemarkerServlet.lastLocale);
+            assertEquals(freemarkerServlet.lastLocale, freemarkerServlet.lastMainTemplate.getLocale());
+        } finally {
+            freemarkerServlet.destroy();
+        }
+    }
+
+    private void assertOutputEncodingEquals(
+            String exptectRespCharacterEncoding,
+            String exptectEnvOutputEncoding,
+            String responseCharacterEncodingInitParam,
+            String templateName) throws ServletException, IOException {
+        assertOutputEncodingEquals(
+                exptectRespCharacterEncoding, exptectEnvOutputEncoding,
+                responseCharacterEncodingInitParam, null,
+                templateName);
+    }
+    
+    private void assertOutputEncodingEquals(
+            String exptectRespCharacterEncoding,
+            String exptectEnvOutputEncoding,
+            String responseCharacterEncodingInitParam,
+            String contentTypeInitParam,
+            String templateName)
+                    throws ServletException, IOException {
+        MockHttpServletRequest request = createMockHttpServletRequest(servletContext, templateName, null);
+        MockHttpServletResponse response = new MockHttpServletResponse();
+
+        MockServletConfig servletConfig = new MockServletConfig(servletContext);
+
+        if (responseCharacterEncodingInitParam != null) {
+            servletConfig.addInitParameter(INIT_PARAM_RESPONSE_CHARACTER_ENCODING, responseCharacterEncodingInitParam);
+        }
+        
+        if (contentTypeInitParam != null) {
+            servletConfig.addInitParameter(INIT_PARAM_CONTENT_TYPE, contentTypeInitParam);
+        }
+
+        TestFreemarkerServlet freemarkerServlet = new TestFreemarkerServlet();
 
         try {
             freemarkerServlet.init(servletConfig);
             freemarkerServlet.doGet(request, response);
 
             assertEquals(HttpServletResponse.SC_OK, response.getStatus());
-            assertEquals(exptectLocale, processedTemplateHolder[0].getLocale());
+            assertEquals(exptectEnvOutputEncoding, freemarkerServlet.lastOutputEncoding);
+            assertEquals(exptectRespCharacterEncoding, response.getCharacterEncoding());
         } finally {
             freemarkerServlet.destroy();
         }
@@ -254,6 +433,7 @@ public class FreemarkerServletTest {
     private MockHttpServletRequest createMockHttpServletRequest(final ServletContext servletContext,
             final String pathInfo, final Locale requestLocale) {
         MockHttpServletRequest servletRequest = new MockHttpServletRequest(servletContext) {
+
             @Override
             public Locale getLocale() {
                 return requestLocale;
@@ -269,4 +449,68 @@ public class FreemarkerServletTest {
         return servletRequest;
     }
 
+    static class TestFreemarkerServlet extends FreemarkerServlet {
+
+        private Template lastMainTemplate;
+        private Locale lastLocale;
+        private String lastOutputEncoding;
+
+        @Override
+        protected Configuration createConfiguration() {
+            Configuration cfg = super.createConfiguration();
+            // Needed for the TemplateConfigurer that sets outputEncoding:
+            cfg.setIncompatibleImprovements(Configuration.VERSION_2_3_22);
+
+            // Set a test runner environment independent default locale:
+            cfg.setLocale(DEFAULT_LOCALE);
+            cfg.setDefaultEncoding(CFG_DEFAULT_ENCODING);
+
+            {
+                TemplateConfigurer outUtf8TC = new TemplateConfigurer();
+                outUtf8TC.setOutputEncoding("UTF-8");
+                
+                TemplateConfigurer srcUtf8TC = new TemplateConfigurer();
+                srcUtf8TC.setEncoding("UTF-8");
+                
+                cfg.setTemplateConfigurers(
+                        new FirstMatchTemplateConfigurerFactory(
+                                new ConditionalTemplateConfigurerFactory(
+                                        new FileNameGlobMatcher(FOO_SRC_UTF8_FTL), srcUtf8TC),
+                                new ConditionalTemplateConfigurerFactory(
+                                        new FileNameGlobMatcher(FOO_OUT_UTF8_FTL), outUtf8TC)
+                        )
+                        .allowNoMatch(true)
+                );
+            }
+
+            return cfg;
+        }
+
+        @Override
+        protected TemplateLoader createTemplateLoader(String templatePath) throws IOException {
+            // Override default template loader
+            if (templatePath.equals("class://")) {
+                StringTemplateLoader tl = new StringTemplateLoader();
+                tl.putTemplate(FOO_FTL, "foo");
+                tl.putTemplate(FOO_SRC_UTF8_FTL, "foo");
+                tl.putTemplate(FOO_OUT_UTF8_FTL, "foo");
+                tl.putTemplate(CONTENT_TYPE_ATTR_FTL, "<#ftl attributes={ 'content_type': 'text/plain' }>foo");
+                tl.putTemplate(OUTPUT_FORMAT_HEADER_FTL, "<#ftl outputFormat='plainText'>foo");
+                return tl;
+            } else {
+                return super.createTemplateLoader(templatePath);
+            }
+        }
+
+        @Override
+        protected void processEnvironment(Environment env, HttpServletRequest request, HttpServletResponse response)
+                throws TemplateException, IOException {
+            lastMainTemplate = env.getMainTemplate();
+            lastLocale = env.getLocale();
+            lastOutputEncoding = env.getOutputEncoding();
+            super.processEnvironment(env, request, response);
+        }
+
+    }
+
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f64aa1f0/src/test/resources/freemarker/ext/servlet/contentTypeAttr.ftl
----------------------------------------------------------------------
diff --git a/src/test/resources/freemarker/ext/servlet/contentTypeAttr.ftl b/src/test/resources/freemarker/ext/servlet/contentTypeAttr.ftl
deleted file mode 100644
index 241f0b7..0000000
--- a/src/test/resources/freemarker/ext/servlet/contentTypeAttr.ftl
+++ /dev/null
@@ -1,20 +0,0 @@
-<#ftl attributes={ 'content_type': 'text/plain' }>
-<#--
-  Licensed to the Apache Software Foundation (ASF) under one
-  or more contributor license agreements.  See the NOTICE file
-  distributed with this work for additional information
-  regarding copyright ownership.  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.
--->
-foo
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f64aa1f0/src/test/resources/freemarker/ext/servlet/foo.ftl
----------------------------------------------------------------------
diff --git a/src/test/resources/freemarker/ext/servlet/foo.ftl b/src/test/resources/freemarker/ext/servlet/foo.ftl
deleted file mode 100644
index a3aac20..0000000
--- a/src/test/resources/freemarker/ext/servlet/foo.ftl
+++ /dev/null
@@ -1,19 +0,0 @@
-<#--
-  Licensed to the Apache Software Foundation (ASF) under one
-  or more contributor license agreements.  See the NOTICE file
-  distributed with this work for additional information
-  regarding copyright ownership.  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.
--->
-foo
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f64aa1f0/src/test/resources/freemarker/ext/servlet/outputFormatHeader.ftl
----------------------------------------------------------------------
diff --git a/src/test/resources/freemarker/ext/servlet/outputFormatHeader.ftl b/src/test/resources/freemarker/ext/servlet/outputFormatHeader.ftl
deleted file mode 100644
index 27d7b2e..0000000
--- a/src/test/resources/freemarker/ext/servlet/outputFormatHeader.ftl
+++ /dev/null
@@ -1,20 +0,0 @@
-<#ftl outputFormat="plainText">
-<#--
-  Licensed to the Apache Software Foundation (ASF) under one
-  or more contributor license agreements.  See the NOTICE file
-  distributed with this work for additional information
-  regarding copyright ownership.  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.
--->
-foo
\ No newline at end of file


Mime
View raw message