You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2017/05/05 21:00:30 UTC

[07/11] incubator-freemarker git commit: Configuration is now immutable. Instead, you should use Configuration.Builder to set up the setting values, then create the Configuration with the builder's build() method. FreemarkerServlet (including some of its

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d61a45d/src/main/java/org/apache/freemarker/core/templateresolver/impl/URLTemplateLoader.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/templateresolver/impl/URLTemplateLoader.java b/src/main/java/org/apache/freemarker/core/templateresolver/impl/URLTemplateLoader.java
index 8f348de..dcb9222 100644
--- a/src/main/java/org/apache/freemarker/core/templateresolver/impl/URLTemplateLoader.java
+++ b/src/main/java/org/apache/freemarker/core/templateresolver/impl/URLTemplateLoader.java
@@ -61,7 +61,7 @@ public abstract class URLTemplateLoader implements TemplateLoader {
     /**
      * Sets if {@link URLConnection#setUseCaches(boolean)} will be called, and with what value. By default this is
      * {@code false}, because FreeMarker has its own template cache with its own update delay setting
-     * ({@link Configuration#setTemplateUpdateDelayMilliseconds(long)}). If this is set to {@code null},
+     * ({@link Configuration#getTemplateUpdateDelayMilliseconds()}). If this is set to {@code null},
      * {@link URLConnection#setUseCaches(boolean)} won't be called.
      */
     public void setURLConnectionUsesCaches(Boolean urlConnectionUsesCaches) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d61a45d/src/main/java/org/apache/freemarker/core/util/CommonBuilder.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/util/CommonBuilder.java b/src/main/java/org/apache/freemarker/core/util/CommonBuilder.java
index 8939121..11aab33 100644
--- a/src/main/java/org/apache/freemarker/core/util/CommonBuilder.java
+++ b/src/main/java/org/apache/freemarker/core/util/CommonBuilder.java
@@ -19,11 +19,17 @@
 
 package org.apache.freemarker.core.util;
 
+import org.apache.freemarker.core.ConfigurationException;
+
 /**
  * Interface of builders (used for implementing the builder pattern).
  */
 public interface CommonBuilder<ProductT> {
 
-    ProductT build();
+    /**
+     * Creates an instance of the product class. This is usually a new instance, though if the product is stateless,
+     * it's possibly a shared object instead of a new one.
+     */
+    ProductT build() throws ConfigurationException;
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d61a45d/src/main/java/org/apache/freemarker/core/util/_StringUtil.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/util/_StringUtil.java b/src/main/java/org/apache/freemarker/core/util/_StringUtil.java
index 2827c84..50c1874 100644
--- a/src/main/java/org/apache/freemarker/core/util/_StringUtil.java
+++ b/src/main/java/org/apache/freemarker/core/util/_StringUtil.java
@@ -29,8 +29,8 @@ import java.util.Map;
 import java.util.StringTokenizer;
 import java.util.regex.Pattern;
 
-import org.apache.freemarker.core.Configuration;
 import org.apache.freemarker.core.Environment;
+import org.apache.freemarker.core.ParsingConfiguration;
 import org.apache.freemarker.core.Template;
 import org.apache.freemarker.core.Version;
 
@@ -1604,21 +1604,21 @@ public class _StringUtil {
     }
 
     /**
-     * @return {@link Configuration#CAMEL_CASE_NAMING_CONVENTION}, or {@link Configuration#LEGACY_NAMING_CONVENTION}
-     *         or, {@link Configuration#AUTO_DETECT_NAMING_CONVENTION} when undecidable.
+     * @return {@link ParsingConfiguration#CAMEL_CASE_NAMING_CONVENTION}, or {@link ParsingConfiguration#LEGACY_NAMING_CONVENTION}
+     *         or, {@link ParsingConfiguration#AUTO_DETECT_NAMING_CONVENTION} when undecidable.
      */
     public static int getIdentifierNamingConvention(String name) {
         final int ln = name.length();
         for (int i = 0; i < ln; i++) {
             final char c = name.charAt(i);
             if (c == '_') {
-                return Configuration.LEGACY_NAMING_CONVENTION;
+                return ParsingConfiguration.LEGACY_NAMING_CONVENTION;
             }
             if (_StringUtil.isUpperUSASCII(c)) {
-                return Configuration.CAMEL_CASE_NAMING_CONVENTION;
+                return ParsingConfiguration.CAMEL_CASE_NAMING_CONVENTION;
             }
         }
-        return Configuration.AUTO_DETECT_NAMING_CONVENTION;
+        return ParsingConfiguration.AUTO_DETECT_NAMING_CONVENTION;
     }
 
     // [2.4] Won't be needed anymore

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d61a45d/src/main/java/org/apache/freemarker/core/valueformat/TemplateValueFormat.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/valueformat/TemplateValueFormat.java b/src/main/java/org/apache/freemarker/core/valueformat/TemplateValueFormat.java
index 8fb0a0d..9203e5a 100644
--- a/src/main/java/org/apache/freemarker/core/valueformat/TemplateValueFormat.java
+++ b/src/main/java/org/apache/freemarker/core/valueformat/TemplateValueFormat.java
@@ -29,5 +29,14 @@ public abstract class TemplateValueFormat {
      * Meant to be used in error messages to tell what format the parsed string didn't fit.
      */
     public abstract String getDescription();
-    
+
+    /**
+     * The implementation in {@link TemplateValueFormat} returns {@code package.className(description)}, where
+     * description comes from {@link #getDescription()}.
+     */
+    @Override
+    public String toString() {
+        return getClass().getName() + "(" + getDescription() + ")";
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d61a45d/src/main/java/org/apache/freemarker/dom/NodeListModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/dom/NodeListModel.java b/src/main/java/org/apache/freemarker/dom/NodeListModel.java
index 75b9e13..333bb5c 100644
--- a/src/main/java/org/apache/freemarker/dom/NodeListModel.java
+++ b/src/main/java/org/apache/freemarker/dom/NodeListModel.java
@@ -44,10 +44,9 @@ import org.w3c.dom.NodeList;
  * Used when the result set contains 0 or multiple nodes; shouldn't be used when you have exactly 1 node. For exactly 1
  * node, use {@link NodeModel#wrap(Node)}, because {@link NodeModel} subclasses can have extra features building on that
  * restriction, like single elements with text content can be used as FTL string-s.
- * 
  * <p>
- * This class is not guaranteed to be thread safe, so instances of this shouldn't be used as shared variable (
- * {@link Configuration#setSharedVariable(String, Object)}).
+ * This class is not guaranteed to be thread safe, so instances of this shouldn't be used as
+ * {@linkplain Configuration#getSharedVariables() shared variable}.
  */
 class NodeListModel extends SimpleSequence implements TemplateHashModel, _UnexpectedTypeErrorExplainerTemplateModel {
     

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d61a45d/src/main/java/org/apache/freemarker/dom/NodeModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/dom/NodeModel.java b/src/main/java/org/apache/freemarker/dom/NodeModel.java
index b1d3cc6..37a5c7d 100644
--- a/src/main/java/org/apache/freemarker/dom/NodeModel.java
+++ b/src/main/java/org/apache/freemarker/dom/NodeModel.java
@@ -55,23 +55,19 @@ import org.w3c.dom.Text;
 
 /**
  * A base class for wrapping a single W3C DOM_WRAPPER Node as a FreeMarker template model.
- * 
  * <p>
  * Note that {@link DefaultObjectWrapper} automatically wraps W3C DOM_WRAPPER {@link Node}-s into this, so you may need do that
  * with this class manually. However, before dropping the {@link Node}-s into the data-model, you certainly want to
  * apply {@link NodeModel#simplify(Node)} on them.
- * 
  * <p>
- * This class is not guaranteed to be thread safe, so instances of this shouldn't be used as shared variable (
- * {@link Configuration#setSharedVariable(String, Object)}).
- * 
+ * This class is not guaranteed to be thread safe, so instances of this shouldn't be used as
+ * {@linkplain Configuration#getSharedVariables() shared variable}.
  * <p>
  * To represent a node sequence (such as a query result) of exactly 1 nodes, this class should be used instead of
  * {@link NodeListModel}, as it adds extra capabilities by utilizing that we have exactly 1 node. If you need to wrap a
  * node sequence of 0 or multiple nodes, you must use {@link NodeListModel}.
  */
-abstract public class NodeModel
-implements TemplateNodeModelEx, TemplateHashModel, TemplateSequenceModel,
+abstract public class NodeModel implements TemplateNodeModelEx, TemplateHashModel, TemplateSequenceModel,
     AdapterTemplateModel, WrapperTemplateModel, _UnexpectedTypeErrorExplainerTemplateModel {
 
     static private final Logger LOG = DomLog.LOG;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d61a45d/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java b/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java
index 3cdc108..b4a2602 100644
--- a/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java
+++ b/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java
@@ -41,21 +41,20 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 
-import org.apache.freemarker.core.MutableProcessingConfiguration;
 import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.Configuration.ExtendableBuilder;
 import org.apache.freemarker.core.ConfigurationException;
 import org.apache.freemarker.core.Environment;
+import org.apache.freemarker.core.MutableProcessingConfiguration;
 import org.apache.freemarker.core.Template;
 import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core.TemplateExceptionHandler;
 import org.apache.freemarker.core.TemplateNotFoundException;
-import org.apache.freemarker.core.Version;
 import org.apache.freemarker.core._CoreLogs;
 import org.apache.freemarker.core.model.ObjectWrapper;
 import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.impl.RestrictedObjectWrapper;
 import org.apache.freemarker.core.model.impl.SimpleHash;
 import org.apache.freemarker.core.outputformat.OutputFormat;
 import org.apache.freemarker.core.outputformat.impl.UndefinedOutputFormat;
@@ -267,12 +266,13 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
  * 
  * <li>The following init-params are supported only for backward compatibility, and their usage is discouraged:
  * {@code TemplateUpdateInterval}, {@code DefaultEncoding}, {@code ObjectWrapper}, {@code TemplateExceptionHandler}.
- * Instead, use init-params with the setting names documented at {@link Configuration#setSetting(String, String)}, such
- * as {@code object_wrapper}.
+ * Instead, use init-params with the setting names documented at
+ * {@link ExtendableBuilder#setSetting(String, String)}, such as {@code object_wrapper}.
  * 
  * <li><strong>Any other init-params</strong> will be interpreted as {@link Configuration}-level FreeMarker setting. See
- * the possible names and values at {@link Configuration#setSetting(String, String)}. Note that these init-param names
- * are starting with lower-case letter (upper-case init-params are used for FreemarkerSerlvet settings).</li>
+ * the possible names and values at {@link ExtendableBuilder#setSetting(String, String)}. Note that
+ * these init-param names are starting with lower-case letter (upper-case init-params are used for FreemarkerSerlvet
+ * settings).</li>
  * 
  * </ul>
  * 
@@ -412,7 +412,6 @@ public class FreemarkerServlet extends HttpServlet {
     private static final String DEPR_INITPARAM_TEMPLATE_DELAY = "TemplateDelay";
     private static final String DEPR_INITPARAM_ENCODING = "DefaultEncoding";
     private static final String DEPR_INITPARAM_OBJECT_WRAPPER = "ObjectWrapper";
-    private static final String DEPR_INITPARAM_WRAPPER_RESTRICTED = "restricted";
     private static final String DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER = "TemplateExceptionHandler";
     private static final String DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER_RETHROW = "rethrow";
     private static final String DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER_DEBUG = "debug";
@@ -508,7 +507,6 @@ public class FreemarkerServlet extends HttpServlet {
     }
 
     // Init-param values:
-    private String templatePath;
     private boolean noCache;
     private Integer bufferSize;
     private boolean exceptionOnMissingTemplate;
@@ -522,13 +520,10 @@ public class FreemarkerServlet extends HttpServlet {
     
     @SuppressFBWarnings(value="SE_BAD_FIELD", justification="Not investing into making this Servlet serializable")
     private Configuration config;
-    @SuppressFBWarnings(value="SE_BAD_FIELD", justification="Not investing into making this Servlet serializable")
-    private ObjectWrapperAndUnwrapper wrapper;
     private ContentType contentType;
     private OverrideResponseContentType overrideResponseContentType = initParamValueToEnum(
             getDefaultOverrideResponseContentType(), OverrideResponseContentType.values());
     private ResponseCharacterEncoding responseCharacterEncoding = ResponseCharacterEncoding.LEGACY;
-    @SuppressFBWarnings(value="SE_BAD_FIELD", justification="Not investing into making this Servlet serializable")
     private Charset forcedResponseCharacterEncoding;
     private OverrideResponseLocale overrideResponseLocale = OverrideResponseLocale.ALWAYS;
     private List/*<MetaInfTldSource>*/ metaInfTldSources;
@@ -544,7 +539,7 @@ public class FreemarkerServlet extends HttpServlet {
 
     /**
      * Don't override this method to adjust FreeMarker settings! Override the protected methods for that, such as
-     * {@link #createConfiguration()}, {@link #createTemplateLoader(String)}, {@link #createDefaultObjectWrapper()},
+     * {@link #createConfigurationBuilder()}, {@link #createTemplateLoader(String)},
      * etc. Also note that lot of things can be changed with init-params instead of overriding methods, so if you
      * override settings, usually you should only override their defaults.
      */
@@ -560,42 +555,34 @@ public class FreemarkerServlet extends HttpServlet {
         }
     }
     
-    private void initialize() throws InitParamValueException, MalformedWebXmlException, ConflictingInitParamsException {
-        config = createConfiguration();
+    private void initialize() throws InitParamValueException, MalformedWebXmlException, ConflictingInitParamsException,
+            ConfigurationException {
+        Configuration.ExtendableBuilder<?> cfgB = createConfigurationBuilder();
         
         // Only override what's coming from the config if it was explicitly specified: 
-        final String iciInitParamValue = getInitParameter(Configuration.INCOMPATIBLE_IMPROVEMENTS_KEY);
+        final String iciInitParamValue = getInitParameter(Configuration.ExtendableBuilder.INCOMPATIBLE_IMPROVEMENTS_KEY);
         if (iciInitParamValue != null) {
             try {
-                config.setSetting(Configuration.INCOMPATIBLE_IMPROVEMENTS_KEY, iciInitParamValue);
+                cfgB.setSetting(Configuration.ExtendableBuilder.INCOMPATIBLE_IMPROVEMENTS_KEY, iciInitParamValue);
             } catch (Exception e) {
-                throw new InitParamValueException(Configuration.INCOMPATIBLE_IMPROVEMENTS_KEY, iciInitParamValue, e);
+                throw new InitParamValueException(Configuration.ExtendableBuilder.INCOMPATIBLE_IMPROVEMENTS_KEY, iciInitParamValue, e);
             }
         }
-        
-        // Set FreemarkerServlet-specific defaults, except where createConfiguration() has already set them:
-        if (!config.isTemplateExceptionHandlerExplicitlySet()) {
-            config.setTemplateExceptionHandler(TemplateExceptionHandler.HTML_DEBUG_HANDLER);
-        }
-        if (!config.isLogTemplateExceptionsExplicitlySet()) {
-            config.setLogTemplateExceptions(false);
-        }
-        
+
         contentType = DEFAULT_CONTENT_TYPE;
         
-        // Process object_wrapper init-param out of order: 
-        wrapper = createObjectWrapper();
-        LOG.debug("Using object wrapper {}", wrapper);
-        config.setObjectWrapper(wrapper);
-        
-        // Process TemplatePath init-param out of order:
-        templatePath = getInitParameter(INIT_PARAM_TEMPLATE_PATH);
-        if (templatePath == null && !config.isTemplateLoaderExplicitlySet()) {
-            templatePath = InitParamParser.TEMPLATE_PATH_PREFIX_CLASS;
+        // Process object_wrapper init-param out of order:
+        String objectWrapperInitParamValue = getInitParameter(
+                MutableProcessingConfiguration.OBJECT_WRAPPER_KEY, DEPR_INITPARAM_OBJECT_WRAPPER);
+        if (objectWrapperInitParamValue != null) {
+            setObjectWrapperFromInitParam(cfgB, objectWrapperInitParamValue);
         }
+
+        // Process TemplatePath init-param out of order:
+        String templatePath = getInitParameter(INIT_PARAM_TEMPLATE_PATH);
         if (templatePath != null) {
             try {
-                config.setTemplateLoader(createTemplateLoader(templatePath));
+                cfgB.setTemplateLoader(createTemplateLoader(templatePath));
             } catch (Exception e) {
                 throw new InitParamValueException(INIT_PARAM_TEMPLATE_PATH, templatePath, e);
             }
@@ -605,9 +592,8 @@ public class FreemarkerServlet extends HttpServlet {
         classpathTlds = createDefaultClassPathTlds();
 
         // Process all other init-params:
-        Enumeration initpnames = getServletConfig().getInitParameterNames();
-        while (initpnames.hasMoreElements()) {
-            final String name = (String) initpnames.nextElement();
+        for (Enumeration initPNames = getServletConfig().getInitParameterNames(); initPNames.hasMoreElements();) {
+            final String name = (String) initPNames.nextElement();
             final String value = getInitParameter(name);
             if (name == null) {
                 throw new MalformedWebXmlException(
@@ -624,21 +610,21 @@ public class FreemarkerServlet extends HttpServlet {
                 if (name.equals(DEPR_INITPARAM_OBJECT_WRAPPER)
                         || name.equals(MutableProcessingConfiguration.OBJECT_WRAPPER_KEY)
                         || name.equals(INIT_PARAM_TEMPLATE_PATH)
-                        || name.equals(Configuration.INCOMPATIBLE_IMPROVEMENTS_KEY)) {
+                        || name.equals(Configuration.ExtendableBuilder.INCOMPATIBLE_IMPROVEMENTS_KEY)) {
                     // ignore: we have already processed these
                 } else if (name.equals(DEPR_INITPARAM_ENCODING)) { // BC
-                    if (getInitParameter(Configuration.SOURCE_ENCODING_KEY) != null) {
+                    if (getInitParameter(Configuration.ExtendableBuilder.SOURCE_ENCODING_KEY) != null) {
                         throw new ConflictingInitParamsException(
-                                Configuration.SOURCE_ENCODING_KEY, DEPR_INITPARAM_ENCODING);
+                                Configuration.ExtendableBuilder.SOURCE_ENCODING_KEY, DEPR_INITPARAM_ENCODING);
                     }
-                    config.setSourceEncoding(Charset.forName(value));
+                    cfgB.setSourceEncoding(Charset.forName(value));
                 } else if (name.equals(DEPR_INITPARAM_TEMPLATE_DELAY)) { // BC
-                    if (getInitParameter(Configuration.TEMPLATE_UPDATE_DELAY_KEY) != null) {
+                    if (getInitParameter(Configuration.ExtendableBuilder.TEMPLATE_UPDATE_DELAY_KEY) != null) {
                         throw new ConflictingInitParamsException(
-                                Configuration.TEMPLATE_UPDATE_DELAY_KEY, DEPR_INITPARAM_TEMPLATE_DELAY);
+                                Configuration.ExtendableBuilder.TEMPLATE_UPDATE_DELAY_KEY, DEPR_INITPARAM_TEMPLATE_DELAY);
                     }
                     try {
-                        config.setTemplateUpdateDelayMilliseconds(Integer.parseInt(value) * 1000L);
+                        cfgB.setTemplateUpdateDelayMilliseconds(Integer.parseInt(value) * 1000L);
                     } catch (NumberFormatException e) {
                         // Intentionally ignored
                     }
@@ -649,13 +635,13 @@ public class FreemarkerServlet extends HttpServlet {
                     }
     
                     if (DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER_RETHROW.equals(value)) {
-                        config.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
+                        cfgB.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
                     } else if (DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER_DEBUG.equals(value)) {
-                        config.setTemplateExceptionHandler(TemplateExceptionHandler.DEBUG_HANDLER);
+                        cfgB.setTemplateExceptionHandler(TemplateExceptionHandler.DEBUG_HANDLER);
                     } else if (DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER_HTML_DEBUG.equals(value)) {
-                        config.setTemplateExceptionHandler(TemplateExceptionHandler.HTML_DEBUG_HANDLER);
+                        cfgB.setTemplateExceptionHandler(TemplateExceptionHandler.HTML_DEBUG_HANDLER);
                     } else if (DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER_IGNORE.equals(value)) {
-                        config.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);
+                        cfgB.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);
                     } else {
                         throw new InitParamValueException(DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER, value,
                                 "Not one of the supported values.");
@@ -695,21 +681,32 @@ public class FreemarkerServlet extends HttpServlet {
                     newClasspathTlds.addAll(InitParamParser.parseCommaSeparatedList(value));
                     classpathTlds = newClasspathTlds;
                 } else {
-                    config.setSetting(name, value);
+                    cfgB.setSetting(name, value);
                 }
             } catch (ConflictingInitParamsException e) {
                 throw e;
             } catch (Exception e) {
                 throw new InitParamValueException(name, value, e);
             }
-        } // while initpnames
+        } // for initPNames
         
         if (contentType.containsCharset && responseCharacterEncoding != ResponseCharacterEncoding.LEGACY) {
             throw new InitParamValueException(INIT_PARAM_CONTENT_TYPE, contentType.httpHeaderValue,
                     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 + "\"."));
-        }        
+        }
+
+        beforeConfigurationBuilt(cfgB);
+        config = cfgB.build();
+        afterConfigurationBuilt(config);
+
+        if (!(config.getObjectWrapper() instanceof ObjectWrapperAndUnwrapper)) {
+            throw new RuntimeException(FreemarkerServlet.class.getSimpleName() + " requires an ObjectWrapper that " +
+                    "implements " + ObjectWrapperAndUnwrapper.class.getName() + ", but this class doesn't do that: "
+                    + config.getObjectWrapper().getClass().getName());
+        }
+        LOG.debug("Using object wrapper {}", config.getObjectWrapper());
     }
     
     private List/*<MetaInfTldSource>*/ parseAsMetaInfTldLocations(String value) throws ParseException {
@@ -751,18 +748,18 @@ public class FreemarkerServlet extends HttpServlet {
     }
 
     /**
-     * Create the template loader. The default implementation will invoke a {@link ClassTemplateLoader} if the template
+     * Create the template loader. The default implementation will create a {@link ClassTemplateLoader} if the template
      * path starts with {@code "class://"}, a {@link FileTemplateLoader} if the template path starts with
      * {@code "file://"}, and a {@link WebAppTemplateLoader} otherwise. Also, if
-     * {@link Configuration#Configuration(org.apache.freemarker.core.Version) incompatible_improvements} is 2.3.22 or higher,
-     * it will invoke a {@link MultiTemplateLoader} if the template path starts with {@code "["}.
+     * {@link Configuration#getIncompatibleImprovements()}  incompatibleImprovements} is 2.3.22 or higher,
+     * it will create a {@link MultiTemplateLoader} if the template path starts with {@code "["}.
      * 
      * @param templatePath
-     *            the template path to invoke a loader for
+     *            the template path to create a loader for
      * @return a newly created template loader
      */
     protected TemplateLoader createTemplateLoader(String templatePath) throws IOException {
-        return InitParamParser.createTemplateLoader(templatePath, getConfiguration(), getClass(), getServletContext());
+        return InitParamParser.createTemplateLoader(templatePath, getClass(), getServletContext());
     }
     
     @Override
@@ -868,9 +865,9 @@ public class FreemarkerServlet extends HttpServlet {
 
         ServletContext servletContext = getServletContext();
         try {
-            logWarnOnObjectWrapperMismatch();
-            
-            TemplateModel model = createModel(wrapper, servletContext, request, response);
+            TemplateModel model = createModel(
+                    (ObjectWrapperAndUnwrapper) config.getObjectWrapper(), // This is checked in initialize()
+                    servletContext, request, response);
 
             // Give subclasses a chance to hook into preprocessing
             if (preTemplateProcess(request, response, template, model)) {
@@ -978,26 +975,6 @@ public class FreemarkerServlet extends HttpServlet {
         throw e;
     }
     
-    @SuppressFBWarnings(value={ "MSF_MUTABLE_SERVLET_FIELD", "DC_DOUBLECHECK" }, justification="Performance trick")
-    private void logWarnOnObjectWrapperMismatch() {
-        // Deliberately uses double check locking.
-        if (wrapper != config.getObjectWrapper() && !objectWrapperMismatchWarnLogged && LOG.isWarnEnabled()) {
-            final boolean logWarn;
-            synchronized (this) {
-                logWarn = !objectWrapperMismatchWarnLogged;
-                if (logWarn) {
-                    objectWrapperMismatchWarnLogged = true;
-                }
-            }
-            if (logWarn) {
-                LOG.warn(
-                        getClass().getName()
-                        + ".wrapper != config.getObjectWrapper(); possibly the result of incorrect extension of "
-                        + FreemarkerServlet.class.getName() + ".");
-            }
-        }
-    }
-    
     /**
      * Returns the locale used for the {@link Configuration#getTemplate(String, Locale)} call (as far as the
      * {@value #INIT_PARAM_OVERRIDE_RESPONSE_LOCALE} Servlet init-param allows that). The base implementation in
@@ -1091,7 +1068,7 @@ public class FreemarkerServlet extends HttpServlet {
     }
 
     /**
-     * Called to invoke the {@link TaglibFactory} once per servlet context.
+     * Called to create the {@link TaglibFactory} once per servlet context.
      * The default implementation configures it based on the servlet-init parameters and various other environmental
      * settings, so if you override this method, you should call super, then adjust the result.
      * 
@@ -1258,128 +1235,60 @@ public class FreemarkerServlet extends HttpServlet {
     }
 
     /**
-     * Creates the FreeMarker {@link Configuration} singleton and (when overidden) maybe sets its defaults. Servlet
-     * init-params will be applied later, and thus can overwrite the settings specified here.
-     * 
+     * Creates a new FreeMarker {@link Configuration} builder; by providing a custom builder, the configuration
+     * setting defaults can be specific to the {@link FreemarkerServlet} subclass.
      * <p>
-     * By overriding this method you can set your preferred {@link Configuration} setting defaults, as only the settings
-     * for which an init-param was specified will be overwritten later. (Note that {@link FreemarkerServlet} also has
-     * its own defaults for a few settings, but since 2.3.22, the servlet detects if those settings were already set
-     * here and then it won't overwrite them.)
-     * 
+     * The default implementation creates a new {@link FreemarkerServletConfigurationBuilder} instance (note that it's
+     * not the standard {@link Configuration} builder, as some setting defaults differ) with
+     * {@link Configuration#getIncompatibleImprovements() incompatibleImprovements}
+     * {@link Configuration#VERSION_3_0_0}.
      * <p>
-     * The default implementation simply creates a new instance with {@link Configuration#Configuration()} and returns
-     * it.
+     * By overriding this method you can use your own {@link FreemarkerServletConfigurationBuilder} subclass
+     * (or actually any {@link ExtendableBuilder} subclass) and hence specify what the defaults are.
      */
-    protected Configuration createConfiguration() {
-        // We can only set incompatible_improvements later, so ignore the deprecation warning here.
-        return new Configuration();
+    protected Configuration.ExtendableBuilder<?> createConfigurationBuilder() {
+        return new FreemarkerServletConfigurationBuilder(this, Configuration.VERSION_3_0_0);
     }
     
     /**
-     * Sets the defaults of the configuration that are specific to the {@link FreemarkerServlet} subclass.
-     * This is called after the common (wired in) {@link FreemarkerServlet} setting defaults was set, also the 
-     */
-    protected void setConfigurationDefaults() {
-        // do nothing
-    }
-    
-    /**
-     * Called from {@link #init()} to invoke the {@link ObjectWrapper}; to customzie this aspect, in most cases you
-     * should override {@link #createDefaultObjectWrapper()} instead. Overriding this method is necessary when you want
-     * to customize how the {@link ObjectWrapper} is created <em>from the init-param values</em>, or you want to do some
-     * post-processing (like checking) on the created {@link ObjectWrapper}. To customize init-param interpretation,
-     * call {@link #getInitParameter(String)} with {@link MutableProcessingConfiguration#OBJECT_WRAPPER_KEY} as argument, and see if it
-     * returns a value that you want to interpret yourself. If was {@code null} or you don't want to interpret the
-     * value, fall back to the super method.
-     * 
+     * Hook for {@link FreemarkerServlet} subclasses to modify the configuration builder just before the
+     * {@link Configuration} is created. Note that to change the defaults of some setting, you meant to use
+     * {@link #createConfigurationBuilder()} instead.
      * <p>
-     * The default implementation interprets the {@code object_wrapper} servlet init-param with
-     * calling {@link MutableProcessingConfiguration#setSetting(String, String)} (see valid values there), or if there's no such servlet
-     * init-param, then it calls {@link #createDefaultObjectWrapper()}.
-     * 
-     * @return The {@link ObjectWrapper} that will be used for adapting request, session, and servlet context attributes
-     *         to {@link TemplateModel}-s, and also as the object wrapper setting of {@link Configuration}.
+     * The implementation in {@link FreemarkerServlet} does nothing here.
      */
-    protected ObjectWrapperAndUnwrapper createObjectWrapper() {
-        String wrapper = getServletConfig().getInitParameter(DEPR_INITPARAM_OBJECT_WRAPPER);
-        if (wrapper != null) { // BC
-            if (getInitParameter(MutableProcessingConfiguration.OBJECT_WRAPPER_KEY) != null) {
-                throw new RuntimeException("Conflicting init-params: "
-                        + MutableProcessingConfiguration.OBJECT_WRAPPER_KEY + " and "
-                        + DEPR_INITPARAM_OBJECT_WRAPPER);
-            }
-            if (DEPR_INITPARAM_WRAPPER_RESTRICTED.equals(wrapper)) {
-                return new RestrictedObjectWrapper.Builder(Configuration.VERSION_3_0_0).build();
-            }
-            return createDefaultObjectWrapper();
-        } else {
-            wrapper = getInitParameter(MutableProcessingConfiguration.OBJECT_WRAPPER_KEY);
-            if (wrapper == null) {
-                if (!config.isObjectWrapperExplicitlySet()) {
-                    return createDefaultObjectWrapper();
-                } else {
-                    return asObjectWrapperAndUnwrapper(config.getObjectWrapper());
-                }
-            } else {
-                try {
-                    config.setSetting(MutableProcessingConfiguration.OBJECT_WRAPPER_KEY, wrapper);
-                } catch (ConfigurationException e) {
-                    throw new RuntimeException("Failed to set " + MutableProcessingConfiguration.OBJECT_WRAPPER_KEY, e);
-                }
-                return asObjectWrapperAndUnwrapper(config.getObjectWrapper());
-            }
-        }
-    }
-
-    private ObjectWrapperAndUnwrapper asObjectWrapperAndUnwrapper(ObjectWrapper objectWrapper) {
-        if (!(objectWrapper instanceof ObjectWrapperAndUnwrapper)) {
-            throw new RuntimeException(FreemarkerServlet.class.getSimpleName() + " requires an ObjectWrapper that " +
-                    "implements " + ObjectWrapperAndUnwrapper.class.getName() + ", but this class doesn't do that: "
-                    + objectWrapper.getClass().getName());
-        }
-        return (ObjectWrapperAndUnwrapper) objectWrapper;
+    protected void beforeConfigurationBuilt(Configuration.ExtendableBuilder<?> cfgB) {
+        // do nothing
     }
 
     /**
-     * Override this to specify what the default {@link ObjectWrapper} will be when the
-     * {@code object_wrapper} Servlet init-param wasn't specified. Note that this is called by
-     * {@link #createConfiguration()}, and so if that was also overidden but improperly then this method might won't be
-     * ever called. Also note that if you set the {@code object_wrapper} in {@link #createConfiguration()}, then this
-     * won't be called, since then that has already specified the default.
-     * 
+     * Hook for {@link FreemarkerServlet} subclasses to examine {@link Configuration} just after it was created.
      * <p>
-     * The default implementation calls {@link Configuration#getDefaultObjectWrapper(Version)}. You
-     * should also pass in the version paramter when creating an {@link ObjectWrapper} that supports that. You can get
-     * the version by calling {@link #getConfiguration()} and then {@link Configuration#getIncompatibleImprovements()}.
-     * 
-     * @since 2.3.22
-     */
-    protected ObjectWrapperAndUnwrapper createDefaultObjectWrapper() {
-        return Configuration.getDefaultObjectWrapper(config.getIncompatibleImprovements());
-    }
-    
-    /**
-     * Should be final; don't override it. Override {@link #createObjectWrapper()} instead.
+     * The implementation in {@link FreemarkerServlet} does nothing here.
      */
-    // [2.4] Make it final
-    protected ObjectWrapper getObjectWrapper() {
-        return wrapper;
+    protected void afterConfigurationBuilt(Configuration cfg) {
+        // do nothing
     }
     
     /**
-     * The value of the {@code TemplatePath} init-param. {@code null} if the {@code template_loader} setting was set in
-     * a custom {@link #createConfiguration()}.
-     * 
-     * @deprecated Not called by FreeMarker code, and there's no point to override this (unless to cause confusion).
+     * Called from {@link #init()} to set the {@link ObjectWrapper} in the {@link ExtendableBuilder}
+     * from the init-param value.
+     * To customize init-param interpretation, see if the init-param value argument is something that you want to
+     * interpret yourself, otherwise fall back to the super method. This method won't be called if there's not
+     * init-param that specifies the object wrapper.
+     * <p>
+     * The default implementation interprets the {@code object_wrapper} servlet init-param with
+     * calling {@link MutableProcessingConfiguration#setSetting(String, String)}.
+     *
+     * @param initParamValue Not {@code null}
      */
-    @Deprecated
-    protected final String getTemplatePath() {
-        return templatePath;
+    protected void setObjectWrapperFromInitParam(Configuration.ExtendableBuilder<?> cb, String initParamValue)
+            throws ConfigurationException {
+        cb.setSetting(MutableProcessingConfiguration.OBJECT_WRAPPER_KEY, initParamValue);
     }
-    
+
     protected HttpRequestParametersHashModel createRequestParametersHashModel(HttpServletRequest request) {
-        return new HttpRequestParametersHashModel(request, getObjectWrapper());
+        return new HttpRequestParametersHashModel(request, config.getObjectWrapper());
     }
 
     /**
@@ -1628,6 +1537,15 @@ public class FreemarkerServlet extends HttpServlet {
         throw new IllegalArgumentException(sb.toString());
     }
 
+    private String getInitParameter(String name1, String name2) {
+        String r1 = getServletConfig().getInitParameter(name1);
+        String r2 = getInitParameter(name2);
+        if (r1 != null && r2 != null) {
+            throw new RuntimeException("Conflicting init-params: " + name1 + " and " + name2);
+        }
+        return r2 != null ? r2 : r1;
+    }
+
     /**
      * Superclass of all (future) init-param value enums.
      * 
@@ -1689,4 +1607,5 @@ public class FreemarkerServlet extends HttpServlet {
         }
     }
 
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d61a45d/src/main/java/org/apache/freemarker/servlet/FreemarkerServletConfigurationBuilder.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/servlet/FreemarkerServletConfigurationBuilder.java b/src/main/java/org/apache/freemarker/servlet/FreemarkerServletConfigurationBuilder.java
new file mode 100644
index 0000000..c3eee4d
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/servlet/FreemarkerServletConfigurationBuilder.java
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+package org.apache.freemarker.servlet;
+
+import java.io.IOException;
+
+import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.Configuration.ExtendableBuilder;
+import org.apache.freemarker.core.TemplateExceptionHandler;
+import org.apache.freemarker.core.Version;
+import org.apache.freemarker.core.templateresolver.TemplateLoader;
+
+/**
+ * Changes some defaults compared to {@link ExtendableBuilder}, to values that makes more sense for the
+ * {@link FreemarkerServlet}.
+ */
+// TODO [FM3] JavaDoc the defaults when they are stable
+public class FreemarkerServletConfigurationBuilder<SelfT extends FreemarkerServletConfigurationBuilder<SelfT>>
+        extends Configuration.ExtendableBuilder<SelfT> {
+
+    private final FreemarkerServlet freemarkerServlet;
+    private TemplateLoader cachedDefaultTemplateLoader;
+
+    public FreemarkerServletConfigurationBuilder(FreemarkerServlet freemarkerServlet, Version version) {
+        super(version);
+        this.freemarkerServlet = freemarkerServlet;
+    }
+
+    @Override
+    protected TemplateExceptionHandler getDefaultTemplateExceptionHandler() {
+        // TODO [FM3] Not a good default. Should depend on if we are in development mode or production mode.
+        return TemplateExceptionHandler.HTML_DEBUG_HANDLER;
+    }
+
+    // TODO [FM3] Remove when this will be the ExtendableBuilder default too.
+    @Override
+    protected boolean getDefaultLogTemplateExceptions() {
+        return false;
+    }
+
+    @Override
+    public void setTemplateLoader(TemplateLoader templateLoader) {
+        super.setTemplateLoader(templateLoader);
+        if (cachedDefaultTemplateLoader != templateLoader) {
+            // Just to make it GC-able
+            cachedDefaultTemplateLoader = null;
+        }
+    }
+
+    @Override
+    protected TemplateLoader getDefaultTemplateLoader() {
+        try {
+            if (cachedDefaultTemplateLoader == null) {
+                cachedDefaultTemplateLoader = freemarkerServlet.createTemplateLoader(InitParamParser.TEMPLATE_PATH_PREFIX_CLASS);
+            }
+            return cachedDefaultTemplateLoader;
+        } catch (IOException e) {
+            // It's almost impossible that this will happen
+            throw new RuntimeException("Failed to create default template loader; see cause exception", e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d61a45d/src/main/java/org/apache/freemarker/servlet/InitParamParser.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/servlet/InitParamParser.java b/src/main/java/org/apache/freemarker/servlet/InitParamParser.java
index 0f20b7b..de8e780 100644
--- a/src/main/java/org/apache/freemarker/servlet/InitParamParser.java
+++ b/src/main/java/org/apache/freemarker/servlet/InitParamParser.java
@@ -27,7 +27,6 @@ import java.util.regex.Pattern;
 
 import javax.servlet.ServletContext;
 
-import org.apache.freemarker.core.Configuration;
 import org.apache.freemarker.core._ObjectBuilderSettingEvaluator;
 import org.apache.freemarker.core._SettingEvaluationEnvironment;
 import org.apache.freemarker.core.templateresolver.TemplateLoader;
@@ -54,7 +53,7 @@ final class InitParamParser {
     }
 
     static TemplateLoader createTemplateLoader(
-            String templatePath, Configuration cfg, Class classLoaderClass, ServletContext srvCtx)
+            String templatePath, Class classLoaderClass, ServletContext srvCtx)
             throws IOException {
         final int settingAssignmentsStart = findTemplatePathSettingAssignmentsStart(templatePath);
         String pureTemplatePath = (settingAssignmentsStart == -1 ? templatePath : templatePath.substring(0, settingAssignmentsStart))
@@ -91,7 +90,7 @@ final class InitParamParser {
             TemplateLoader[] templateLoaders = new TemplateLoader[listItems.size()];
             for (int i = 0; i < listItems.size(); i++) {
                 String pathItem = (String) listItems.get(i);
-                templateLoaders[i] = createTemplateLoader(pathItem, cfg, classLoaderClass, srvCtx);
+                templateLoaders[i] = createTemplateLoader(pathItem, classLoaderClass, srvCtx);
             }
             templateLoader = new MultiTemplateLoader(templateLoaders);
         } else if (pureTemplatePath.startsWith("{")) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d61a45d/src/main/java/org/apache/freemarker/servlet/WebAppTemplateLoader.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/servlet/WebAppTemplateLoader.java b/src/main/java/org/apache/freemarker/servlet/WebAppTemplateLoader.java
index be215d5..9be5834 100644
--- a/src/main/java/org/apache/freemarker/servlet/WebAppTemplateLoader.java
+++ b/src/main/java/org/apache/freemarker/servlet/WebAppTemplateLoader.java
@@ -155,7 +155,7 @@ public class WebAppTemplateLoader implements TemplateLoader {
      * workaround for the case when the servlet container doesn't show template modifications after the template was
      * already loaded earlier. But it's certainly better to counter this problem by disabling the URL connection cache
      * with {@link #setURLConnectionUsesCaches(Boolean)}, which is also the default behavior with
-     * {@link Configuration#setIncompatibleImprovements(org.apache.freemarker.core.Version) incompatible_improvements} 2.3.21
+     * {@link Configuration#getIncompatibleImprovements() incompatibleImprovements} 2.3.21
      * and later.
      * 
      * @since 2.3.23

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d61a45d/src/manual/en_US/FM3-CHANGE-LOG.txt
----------------------------------------------------------------------
diff --git a/src/manual/en_US/FM3-CHANGE-LOG.txt b/src/manual/en_US/FM3-CHANGE-LOG.txt
index 9399918..e5a898e 100644
--- a/src/manual/en_US/FM3-CHANGE-LOG.txt
+++ b/src/manual/en_US/FM3-CHANGE-LOG.txt
@@ -212,4 +212,15 @@ the FreeMarer 3 changelog here:
   lookup and the source name, its meaning wasn't clean (but it meant the lookup name). TemplateException and ParseException 
   now also have the same properites: getTemplateSourceName(), getTemplateLookupName(), and even getSourceOrLookupName().
   Location information in error messages show getTemplateSourceOrLookupName().
-- Configuration.setSharedVaribles (not the typo in it) was renamed to setSharedVariables
\ No newline at end of file
+- Configuration.setSharedVaribles (not the typo in it) was renamed to setSharedVariables
+- Configuration is now immutable. Instead, you should use Configuration.Builder to set up the setting values, then create
+  the Configuration with the builder's build() method. Further notes:
+  - Most of the mutator methods (like setters) were moved from Configuration to Configuration.Builder. However,
+    setClassForTemplateLoader, setDirectoryForTemplateLoading and the like were removed, instead there's just
+    setTemplateLoader. So for example. instead of  setClassForTemplateLoader(Foo.class, "templates") now you have
+    to write setTemplateLoader(new ClassTemplateLoader(Foo.class, "templates")). While it's a bit longer, it shows
+    more clearly what's happening, and always supports all TemplateLoader constructor overloads.
+  - It's now possible to change the Configuration setting defaults by using a custom Configuration.ExtendableBuilder
+    subclass instead of Configuration.Builder (which is also an ExtendableBuilder subclass). FreemarkerServlet has
+    switched to this approach, using its own builder subclass to provide defaults that makes the sense in that particular
+    application. Its API-s which were used for customizing FreemarkerServlet has bean changed accordingly.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d61a45d/src/test/java/org/apache/freemarker/core/ASTPrinter.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/ASTPrinter.java b/src/test/java/org/apache/freemarker/core/ASTPrinter.java
index ddd8e9b..3518b29 100644
--- a/src/test/java/org/apache/freemarker/core/ASTPrinter.java
+++ b/src/test/java/org/apache/freemarker/core/ASTPrinter.java
@@ -43,6 +43,7 @@ import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.util.FTLUtil;
 import org.apache.freemarker.core.util._ClassUtil;
 import org.apache.freemarker.core.util._StringUtil;
+import org.apache.freemarker.test.TestConfigurationBuilder;
 
 /**
  * Static methods and command-line tool for printing the AST of a template. 
@@ -68,7 +69,7 @@ public class ASTPrinter {
     }
     
     private ASTPrinter() {
-        cfg = new Configuration(Configuration.VERSION_3_0_0);
+        cfg = new TestConfigurationBuilder(Configuration.VERSION_3_0_0).build();
     }
     
     private void mainSingleTemplate(String[] args) throws IOException, FileNotFoundException {
@@ -230,8 +231,7 @@ public class ASTPrinter {
     }
     
     public static String getASTAsString(String templateName, String ftl, Options opts) throws IOException {
-        Configuration cfg = new Configuration();
-        Template t = new Template(templateName, ftl, cfg);
+        Template t = new Template(templateName, ftl, new TestConfigurationBuilder().build());
         return getASTAsString(t, opts);
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d61a45d/src/test/java/org/apache/freemarker/core/ActualNamingConvetionTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/ActualNamingConvetionTest.java b/src/test/java/org/apache/freemarker/core/ActualNamingConvetionTest.java
index ea07e1f..57e40fa 100644
--- a/src/test/java/org/apache/freemarker/core/ActualNamingConvetionTest.java
+++ b/src/test/java/org/apache/freemarker/core/ActualNamingConvetionTest.java
@@ -23,6 +23,7 @@ import static org.junit.Assert.*;
 
 import java.io.IOException;
 
+import org.apache.freemarker.test.TestConfigurationBuilder;
 import org.junit.Test;
 
 public class ActualNamingConvetionTest {
@@ -31,35 +32,35 @@ public class ActualNamingConvetionTest {
     public void testUndetectable() throws IOException {
         final String ftl = "<#if true>${x?size}</#if>";
         assertEquals(getActualNamingConvention(ftl,
-                Configuration.AUTO_DETECT_NAMING_CONVENTION), Configuration.AUTO_DETECT_NAMING_CONVENTION);
+                ParsingConfiguration.AUTO_DETECT_NAMING_CONVENTION), ParsingConfiguration.AUTO_DETECT_NAMING_CONVENTION);
         assertEquals(getActualNamingConvention(ftl,
-                Configuration.LEGACY_NAMING_CONVENTION), Configuration.LEGACY_NAMING_CONVENTION);
+                ParsingConfiguration.LEGACY_NAMING_CONVENTION), ParsingConfiguration.LEGACY_NAMING_CONVENTION);
         assertEquals(getActualNamingConvention(ftl,
-                Configuration.CAMEL_CASE_NAMING_CONVENTION), Configuration.CAMEL_CASE_NAMING_CONVENTION);
+                ParsingConfiguration.CAMEL_CASE_NAMING_CONVENTION), ParsingConfiguration.CAMEL_CASE_NAMING_CONVENTION);
     }
 
     @Test
     public void testLegacyDetected() throws IOException {
         final String ftl = "${x?upper_case}";
         assertEquals(getActualNamingConvention(ftl,
-                Configuration.AUTO_DETECT_NAMING_CONVENTION), Configuration.LEGACY_NAMING_CONVENTION);
+                ParsingConfiguration.AUTO_DETECT_NAMING_CONVENTION), ParsingConfiguration.LEGACY_NAMING_CONVENTION);
         assertEquals(getActualNamingConvention(ftl,
-                Configuration.LEGACY_NAMING_CONVENTION), Configuration.LEGACY_NAMING_CONVENTION);
+                ParsingConfiguration.LEGACY_NAMING_CONVENTION), ParsingConfiguration.LEGACY_NAMING_CONVENTION);
     }
 
     @Test
     public void testCamelCaseDetected() throws IOException {
         final String ftl = "${x?upperCase}";
         assertEquals(getActualNamingConvention(ftl,
-                Configuration.AUTO_DETECT_NAMING_CONVENTION), Configuration.CAMEL_CASE_NAMING_CONVENTION);
+                ParsingConfiguration.AUTO_DETECT_NAMING_CONVENTION), ParsingConfiguration.CAMEL_CASE_NAMING_CONVENTION);
         assertEquals(getActualNamingConvention(ftl,
-                Configuration.CAMEL_CASE_NAMING_CONVENTION), Configuration.CAMEL_CASE_NAMING_CONVENTION);
+                ParsingConfiguration.CAMEL_CASE_NAMING_CONVENTION), ParsingConfiguration.CAMEL_CASE_NAMING_CONVENTION);
     }
 
     private int getActualNamingConvention(String ftl, int namingConvention) throws IOException {
-        Configuration cfg = new Configuration();
-        cfg.setNamingConvention(namingConvention);
-        return new Template(null, ftl, cfg).getActualNamingConvention();
+        return new Template(null, ftl,
+                new TestConfigurationBuilder().namingConvention(namingConvention).build())
+                .getActualNamingConvention();
     }
     
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d61a45d/src/test/java/org/apache/freemarker/core/ActualTagSyntaxTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/ActualTagSyntaxTest.java b/src/test/java/org/apache/freemarker/core/ActualTagSyntaxTest.java
index 58c101f..88f0646 100644
--- a/src/test/java/org/apache/freemarker/core/ActualTagSyntaxTest.java
+++ b/src/test/java/org/apache/freemarker/core/ActualTagSyntaxTest.java
@@ -19,48 +19,50 @@
 
 package org.apache.freemarker.core;
 
+import static org.apache.freemarker.core.ParsingConfiguration.*;
 import static org.junit.Assert.*;
 
 import java.io.IOException;
 
+import org.apache.freemarker.test.TestConfigurationBuilder;
 import org.junit.Test;
 
 public class ActualTagSyntaxTest {
 
     @Test
     public void testWithFtlHeader() throws IOException {
-        testWithFtlHeader(Configuration.AUTO_DETECT_TAG_SYNTAX);
-        testWithFtlHeader(Configuration.ANGLE_BRACKET_TAG_SYNTAX);
-        testWithFtlHeader(Configuration.SQUARE_BRACKET_TAG_SYNTAX);
+        testWithFtlHeader(AUTO_DETECT_TAG_SYNTAX);
+        testWithFtlHeader(ANGLE_BRACKET_TAG_SYNTAX);
+        testWithFtlHeader(SQUARE_BRACKET_TAG_SYNTAX);
     }
     
     private void testWithFtlHeader(int cfgTagSyntax) throws IOException {
-        assertEquals(getActualTagSyntax("[#ftl]foo", cfgTagSyntax), Configuration.SQUARE_BRACKET_TAG_SYNTAX);
-        assertEquals(getActualTagSyntax("<#ftl>foo", cfgTagSyntax), Configuration.ANGLE_BRACKET_TAG_SYNTAX);
+        assertEquals(getActualTagSyntax("[#ftl]foo", cfgTagSyntax), SQUARE_BRACKET_TAG_SYNTAX);
+        assertEquals(getActualTagSyntax("<#ftl>foo", cfgTagSyntax), ANGLE_BRACKET_TAG_SYNTAX);
     }
     
     @Test
     public void testUndecidable() throws IOException {
-        assertEquals(getActualTagSyntax("foo", Configuration.AUTO_DETECT_TAG_SYNTAX), Configuration.ANGLE_BRACKET_TAG_SYNTAX);
-        assertEquals(getActualTagSyntax("foo", Configuration.ANGLE_BRACKET_TAG_SYNTAX), Configuration.ANGLE_BRACKET_TAG_SYNTAX);
-        assertEquals(getActualTagSyntax("foo", Configuration.SQUARE_BRACKET_TAG_SYNTAX), Configuration.SQUARE_BRACKET_TAG_SYNTAX);
+        assertEquals(getActualTagSyntax("foo", AUTO_DETECT_TAG_SYNTAX), ANGLE_BRACKET_TAG_SYNTAX);
+        assertEquals(getActualTagSyntax("foo", ANGLE_BRACKET_TAG_SYNTAX), ANGLE_BRACKET_TAG_SYNTAX);
+        assertEquals(getActualTagSyntax("foo", SQUARE_BRACKET_TAG_SYNTAX), SQUARE_BRACKET_TAG_SYNTAX);
     }
 
     @Test
     public void testDecidableWithoutFtlHeader() throws IOException {
-        assertEquals(getActualTagSyntax("foo<#if true></#if>", Configuration.AUTO_DETECT_TAG_SYNTAX), Configuration.ANGLE_BRACKET_TAG_SYNTAX);
-        assertEquals(getActualTagSyntax("foo<#if true></#if>", Configuration.ANGLE_BRACKET_TAG_SYNTAX), Configuration.ANGLE_BRACKET_TAG_SYNTAX);
-        assertEquals(getActualTagSyntax("foo<#if true></#if>", Configuration.SQUARE_BRACKET_TAG_SYNTAX), Configuration.SQUARE_BRACKET_TAG_SYNTAX);
+        assertEquals(getActualTagSyntax("foo<#if true></#if>", AUTO_DETECT_TAG_SYNTAX), ANGLE_BRACKET_TAG_SYNTAX);
+        assertEquals(getActualTagSyntax("foo<#if true></#if>", ANGLE_BRACKET_TAG_SYNTAX), ANGLE_BRACKET_TAG_SYNTAX);
+        assertEquals(getActualTagSyntax("foo<#if true></#if>", SQUARE_BRACKET_TAG_SYNTAX), SQUARE_BRACKET_TAG_SYNTAX);
         
-        assertEquals(getActualTagSyntax("foo[#if true][/#if]", Configuration.AUTO_DETECT_TAG_SYNTAX), Configuration.SQUARE_BRACKET_TAG_SYNTAX);
-        assertEquals(getActualTagSyntax("foo[#if true][/#if]", Configuration.ANGLE_BRACKET_TAG_SYNTAX), Configuration.ANGLE_BRACKET_TAG_SYNTAX);
-        assertEquals(getActualTagSyntax("foo[#if true][/#if]", Configuration.SQUARE_BRACKET_TAG_SYNTAX), Configuration.SQUARE_BRACKET_TAG_SYNTAX);
+        assertEquals(getActualTagSyntax("foo[#if true][/#if]", AUTO_DETECT_TAG_SYNTAX), SQUARE_BRACKET_TAG_SYNTAX);
+        assertEquals(getActualTagSyntax("foo[#if true][/#if]", ANGLE_BRACKET_TAG_SYNTAX), ANGLE_BRACKET_TAG_SYNTAX);
+        assertEquals(getActualTagSyntax("foo[#if true][/#if]", SQUARE_BRACKET_TAG_SYNTAX), SQUARE_BRACKET_TAG_SYNTAX);
     }
     
     private int getActualTagSyntax(String ftl, int cfgTagSyntax) throws IOException {
-        Configuration cfg = new Configuration();
-        cfg.setTagSyntax(cfgTagSyntax);
-        return new Template(null, ftl, cfg).getActualTagSyntax();
+        return new Template(
+                null, ftl,
+                new TestConfigurationBuilder().tagSyntax(cfgTagSyntax).build()).getActualTagSyntax();
     }
     
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d61a45d/src/test/java/org/apache/freemarker/core/BreakPlacementTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/BreakPlacementTest.java b/src/test/java/org/apache/freemarker/core/BreakPlacementTest.java
index 05da5bc..61ba02b 100644
--- a/src/test/java/org/apache/freemarker/core/BreakPlacementTest.java
+++ b/src/test/java/org/apache/freemarker/core/BreakPlacementTest.java
@@ -50,13 +50,7 @@ public class BreakPlacementTest extends TemplateTest {
         assertErrorContains("<#if false><#break></#if>", BREAK_NESTING_ERROR_MESSAGE_PART);
         assertErrorContains("<#list xs><#break></#list>", BREAK_NESTING_ERROR_MESSAGE_PART);
         assertErrorContains("<#list 1..2 as x>${x}<#else><#break></#list>", BREAK_NESTING_ERROR_MESSAGE_PART);
+        assertErrorContains("<#list 1..2 as x>${x}<#macro m><#break></#macro></#list>", BREAK_NESTING_ERROR_MESSAGE_PART);
     }
 
-    @Test
-    public void testInvalidPlacementInsideMacro() throws IOException, TemplateException {
-        final String ftl = "<#list 1..2 as x>${x}<#macro m><#break></#macro></#list>";
-        getConfiguration().setIncompatibleImprovements(Configuration.VERSION_3_0_0);
-        assertErrorContains(ftl, BREAK_NESTING_ERROR_MESSAGE_PART);
-    }
-    
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d61a45d/src/test/java/org/apache/freemarker/core/CamelCaseTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/CamelCaseTest.java b/src/test/java/org/apache/freemarker/core/CamelCaseTest.java
index 7c07127..95572ad 100644
--- a/src/test/java/org/apache/freemarker/core/CamelCaseTest.java
+++ b/src/test/java/org/apache/freemarker/core/CamelCaseTest.java
@@ -31,15 +31,18 @@ import org.apache.freemarker.core.outputformat.impl.HTMLOutputFormat;
 import org.apache.freemarker.core.outputformat.impl.UndefinedOutputFormat;
 import org.apache.freemarker.core.util._StringUtil;
 import org.apache.freemarker.test.TemplateTest;
+import org.apache.freemarker.test.TestConfigurationBuilder;
 import org.junit.Test;
 
 public class CamelCaseTest extends TemplateTest {
 
     @Test
     public void camelCaseSpecialVars() throws IOException, TemplateException {
-        getConfiguration().setOutputEncoding(StandardCharsets.UTF_8);
-        getConfiguration().setURLEscapingCharset(StandardCharsets.ISO_8859_1);
-        getConfiguration().setLocale(Locale.GERMANY);
+        setConfiguration(new TestConfigurationBuilder()
+                .outputEncoding(StandardCharsets.UTF_8)
+                .urlEscapingCharset(StandardCharsets.ISO_8859_1)
+                .locale(Locale.GERMANY)
+                .build());
         assertOutput("${.dataModel?isHash?c}", "true");
         assertOutput("${.data_model?is_hash?c}", "true");
         assertOutput("${.localeObject.toString()}", "de_DE");
@@ -69,10 +72,11 @@ public class CamelCaseTest extends TemplateTest {
         
         assertErrorContains("<#if x><#elseIf y></#if>${.foo}", "dataModel", "\\!data_model");
         assertErrorContains("<#if x><#elseif y></#if>${.foo}", "data_model", "\\!dataModel");
-        
-        getConfiguration().setNamingConvention(Configuration.CAMEL_CASE_NAMING_CONVENTION);
+
+        setConfigurationToCamelCaseNamingConvention();
         assertErrorContains("${.foo}", "dataModel", "\\!data_model");
-        getConfiguration().setNamingConvention(Configuration.LEGACY_NAMING_CONVENTION);
+
+        setConfigurationToLegacyCaseNamingConvention();
         assertErrorContains("${.foo}", "data_model", "\\!dataModel");
     }
     
@@ -87,8 +91,6 @@ public class CamelCaseTest extends TemplateTest {
     
     @Test
     public void camelCaseFtlHeaderParameters() throws IOException, TemplateException {
-        getConfiguration().setOutputEncoding(StandardCharsets.UTF_8);
-        
         assertOutput(
                 "<#ftl "
                 + "stripWhitespace=false "
@@ -116,16 +118,25 @@ public class CamelCaseTest extends TemplateTest {
         assertErrorContains("<#ftl strip_whitespace=true stripText=true>", "naming convention");
         assertErrorContains("<#ftl stripWhitespace=true>${.foo_bar}", "naming convention");
         assertErrorContains("<#ftl strip_whitespace=true>${.fooBar}", "naming convention");
-        
-        getConfiguration().setNamingConvention(Configuration.CAMEL_CASE_NAMING_CONVENTION);
+
+        setConfiguration(new TestConfigurationBuilder()
+                .namingConvention(ParsingConfiguration.CAMEL_CASE_NAMING_CONVENTION)
+                .outputEncoding(StandardCharsets.UTF_8)
+                .build());
         assertErrorContains("<#ftl strip_whitespace=true>", "naming convention");
         assertOutput("<#ftl stripWhitespace=true>${.outputEncoding}", StandardCharsets.UTF_8.name());
         
-        getConfiguration().setNamingConvention(Configuration.LEGACY_NAMING_CONVENTION);
+        setConfiguration(new TestConfigurationBuilder()
+                .namingConvention(ParsingConfiguration.LEGACY_NAMING_CONVENTION)
+                .outputEncoding(StandardCharsets.UTF_8)
+                .build());
         assertErrorContains("<#ftl stripWhitespace=true>", "naming convention");
         assertOutput("<#ftl strip_whitespace=true>${.output_encoding}", StandardCharsets.UTF_8.name());
         
-        getConfiguration().setNamingConvention(Configuration.AUTO_DETECT_NAMING_CONVENTION);
+        setConfiguration(new TestConfigurationBuilder()
+                .namingConvention(ParsingConfiguration.AUTO_DETECT_NAMING_CONVENTION)
+                .outputEncoding(StandardCharsets.UTF_8)
+                .build());
         assertOutput("<#ftl stripWhitespace=true>${.outputEncoding}", StandardCharsets.UTF_8.name());
         assertOutput("<#ftl encoding='iso-8859-1' stripWhitespace=true>${.outputEncoding}", StandardCharsets.UTF_8.name());
         assertOutput("<#ftl stripWhitespace=true encoding='iso-8859-1'>${.outputEncoding}", StandardCharsets.UTF_8.name());
@@ -143,9 +154,10 @@ public class CamelCaseTest extends TemplateTest {
         assertErrorContains("<#if x><#elseIf y></#if><#setting foo=1>", "booleanFormat", "\\!boolean_format");
         assertErrorContains("<#if x><#elseif y></#if><#setting foo=1>", "boolean_format", "\\!booleanFormat");
 
-        getConfiguration().setNamingConvention(Configuration.CAMEL_CASE_NAMING_CONVENTION);
+        setConfigurationToCamelCaseNamingConvention();
         assertErrorContains("<#setting foo=1>", "booleanFormat", "\\!boolean_format");
-        getConfiguration().setNamingConvention(Configuration.LEGACY_NAMING_CONVENTION);
+
+        setConfigurationToLegacyCaseNamingConvention();
         assertErrorContains("<#setting foo=1>", "boolean_format", "\\!booleanFormat");
     }
     
@@ -175,8 +187,8 @@ public class CamelCaseTest extends TemplateTest {
 
     @Test
     public void stringLiteralInterpolation() throws IOException, TemplateException {
-        assertEquals(Configuration.AUTO_DETECT_NAMING_CONVENTION, getConfiguration().getNamingConvention());
-        getConfiguration().setSharedVariable("x", "x");
+        assertEquals(ParsingConfiguration.AUTO_DETECT_NAMING_CONVENTION, getConfiguration().getNamingConvention());
+        addToDataModel("x", "x");
         
         assertOutput("${'-${x?upperCase}-'} ${x?upperCase}", "-X- X");
         assertOutput("${x?upperCase} ${'-${x?upperCase}-'}", "X -X-");
@@ -191,13 +203,13 @@ public class CamelCaseTest extends TemplateTest {
                 "naming convention", "camel", "upper_case");
         assertErrorContains("${x?upperCase} ${'-${x?upper_case}-'}",
                 "naming convention", "camel", "upper_case");
-        
-        getConfiguration().setNamingConvention(Configuration.CAMEL_CASE_NAMING_CONVENTION);
+
+        setConfigurationToCamelCaseNamingConvention();
         assertOutput("${'-${x?upperCase}-'} ${x?upperCase}", "-X- X");
         assertErrorContains("${'-${x?upper_case}-'}",
                 "naming convention", "camel", "upper_case", "\\!detection");
-        
-        getConfiguration().setNamingConvention(Configuration.LEGACY_NAMING_CONVENTION);
+
+        setConfigurationToLegacyCaseNamingConvention();
         assertOutput("${'-${x?upper_case}-'} ${x?upper_case}", "-X- X");
         assertErrorContains("${'-${x?upperCase}-'}",
                 "naming convention", "legacy", "upperCase", "\\!detection");
@@ -205,7 +217,7 @@ public class CamelCaseTest extends TemplateTest {
     
     @Test
     public void evalAndInterpret() throws IOException, TemplateException {
-        assertEquals(Configuration.AUTO_DETECT_NAMING_CONVENTION, getConfiguration().getNamingConvention());
+        assertEquals(ParsingConfiguration.AUTO_DETECT_NAMING_CONVENTION, getConfiguration().getNamingConvention());
         // The naming convention detected doesn't affect the enclosing template's naming convention.
         // - ?eval:
         assertOutput("${\"'x'?upperCase\"?eval}${'x'?upper_case}", "XX");
@@ -221,7 +233,7 @@ public class CamelCaseTest extends TemplateTest {
                 "naming convention", "camel", "upperCase", "is_string", "line 2", "line 3");
         
         // Will be inherited by ?eval-ed/?interpreted fragments:
-        getConfiguration().setNamingConvention(Configuration.CAMEL_CASE_NAMING_CONVENTION);
+        setConfigurationToCamelCaseNamingConvention();
         // - ?eval:
         assertErrorContains("${\"'x'?upper_case\"?eval}", "naming convention", "camel", "upper_case");
         assertOutput("${\"'x'?upperCase\"?eval}", "X");
@@ -230,7 +242,7 @@ public class CamelCaseTest extends TemplateTest {
         assertOutput("<@r\"${'x'?upperCase}\"?interpret />", "X");
         
         // Again, will be inherited by ?eval-ed/?interpreted fragments:
-        getConfiguration().setNamingConvention(Configuration.LEGACY_NAMING_CONVENTION);
+        setConfigurationToLegacyCaseNamingConvention();
         // - ?eval:
         assertErrorContains("${\"'x'?upperCase\"?eval}", "naming convention", "legacy", "upperCase");
         assertOutput("${\"'x'?upper_case\"?eval}", "X");
@@ -238,7 +250,13 @@ public class CamelCaseTest extends TemplateTest {
         assertErrorContains("<@r\"${'x'?upperCase}\"?interpret />", "naming convention", "legacy", "upperCase");
         assertOutput("<@r\"${'x'?upper_case}\"?interpret />", "X");
     }
-    
+
+    private void setConfigurationToLegacyCaseNamingConvention() {
+        setConfiguration(new TestConfigurationBuilder()
+                .namingConvention(ParsingConfiguration.LEGACY_NAMING_CONVENTION)
+                .build());
+    }
+
     @Test
     public void camelCaseBuiltInErrorMessage() throws IOException, TemplateException {
         assertErrorContains("${'x'?upperCasw}", "upperCase", "\\!upper_case");
@@ -248,13 +266,19 @@ public class CamelCaseTest extends TemplateTest {
         
         assertErrorContains("<#if x><#elseIf y></#if> ${'x'?foo}", "upperCase", "\\!upper_case");
         assertErrorContains("<#if x><#elseif y></#if>${'x'?foo}", "upper_case", "\\!upperCase");
-        
-        getConfiguration().setNamingConvention(Configuration.CAMEL_CASE_NAMING_CONVENTION);
+
+        setConfigurationToCamelCaseNamingConvention();
         assertErrorContains("${'x'?foo}", "upperCase", "\\!upper_case");
-        getConfiguration().setNamingConvention(Configuration.LEGACY_NAMING_CONVENTION);
+        setConfigurationToLegacyCaseNamingConvention();
         assertErrorContains("${'x'?foo}", "upper_case", "\\!upperCase");
     }
-    
+
+    private void setConfigurationToCamelCaseNamingConvention() {
+        setConfiguration(new TestConfigurationBuilder()
+                .namingConvention(ParsingConfiguration.CAMEL_CASE_NAMING_CONVENTION)
+                .build());
+    }
+
     @Test
     public void builtInsHasBothNamingStyle() throws IOException, TemplateException {
         assertContainsBothNamingStyles(getConfiguration().getSupportedBuiltInNames(), new NamePairAssertion() {
@@ -273,7 +297,7 @@ public class CamelCaseTest extends TemplateTest {
     private void assertContainsBothNamingStyles(Set<String> names, NamePairAssertion namePairAssertion) {
         Set<String> underscoredNamesWithCamelCasePair = new HashSet<>();
         for (String name : names) {
-            if (_StringUtil.getIdentifierNamingConvention(name) == Configuration.CAMEL_CASE_NAMING_CONVENTION) {
+            if (_StringUtil.getIdentifierNamingConvention(name) == ParsingConfiguration.CAMEL_CASE_NAMING_CONVENTION) {
                 String underscoredName = correctIsoBIExceptions(_StringUtil.camelCaseToUnderscored(name)); 
                 assertTrue(
                         "Missing underscored variation \"" + underscoredName + "\" for \"" + name + "\".",
@@ -284,7 +308,7 @@ public class CamelCaseTest extends TemplateTest {
             }
         }
         for (String name : names) {
-            if (_StringUtil.getIdentifierNamingConvention(name) == Configuration.LEGACY_NAMING_CONVENTION) {
+            if (_StringUtil.getIdentifierNamingConvention(name) == ParsingConfiguration.LEGACY_NAMING_CONVENTION) {
                 assertTrue("Missing camel case variation for \"" + name + "\".",
                         underscoredNamesWithCamelCasePair.contains(name));
             }
@@ -298,7 +322,9 @@ public class CamelCaseTest extends TemplateTest {
     @Test
     public void camelCaseDirectives() throws IOException, TemplateException {
         camelCaseDirectives(false);
-        getConfiguration().setTagSyntax(Configuration.AUTO_DETECT_TAG_SYNTAX);
+        setConfiguration(new TestConfigurationBuilder()
+                .tagSyntax(ParsingConfiguration.AUTO_DETECT_TAG_SYNTAX)
+                .build());
         camelCaseDirectives(true);
     }
 
@@ -338,12 +364,13 @@ public class CamelCaseTest extends TemplateTest {
     }
     
     private void explicitNamingConvention(boolean squared) throws IOException, TemplateException {
-        if (squared) {
-            getConfiguration().setTagSyntax(Configuration.AUTO_DETECT_TAG_SYNTAX);
-        }
-        
-        getConfiguration().setNamingConvention(Configuration.CAMEL_CASE_NAMING_CONVENTION);
-        
+        int tagSyntax = squared ? ParsingConfiguration.AUTO_DETECT_TAG_SYNTAX
+                : ParsingConfiguration.ANGLE_BRACKET_TAG_SYNTAX;
+        setConfiguration(new TestConfigurationBuilder()
+                .tagSyntax(tagSyntax)
+                .namingConvention(ParsingConfiguration.CAMEL_CASE_NAMING_CONVENTION)
+                .build());
+
         assertErrorContains(
                 squared("<#if true>t<#elseif false>f</#if>", squared),
                 "naming convention", "camel", "#elseif");
@@ -366,9 +393,12 @@ public class CamelCaseTest extends TemplateTest {
                 "1");
 
         // ---
-        
-        getConfiguration().setNamingConvention(Configuration.LEGACY_NAMING_CONVENTION);
-        
+
+        setConfiguration(new TestConfigurationBuilder()
+                .tagSyntax(tagSyntax)
+                .namingConvention(ParsingConfiguration.LEGACY_NAMING_CONVENTION)
+                .build());
+
         assertErrorContains(
                 squared("<#if true>t<#elseIf false>f</#if>", squared),
                 "naming convention", "legacy", "#elseIf");

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d61a45d/src/test/java/org/apache/freemarker/core/CanonicalFormTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/CanonicalFormTest.java b/src/test/java/org/apache/freemarker/core/CanonicalFormTest.java
index 3218cd6..c78c90e 100644
--- a/src/test/java/org/apache/freemarker/core/CanonicalFormTest.java
+++ b/src/test/java/org/apache/freemarker/core/CanonicalFormTest.java
@@ -24,6 +24,7 @@ import java.io.StringWriter;
 
 import org.apache.freemarker.core.templateresolver.impl.ClassTemplateLoader;
 import org.apache.freemarker.test.CopyrightCommentRemoverTemplateLoader;
+import org.apache.freemarker.test.TestConfigurationBuilder;
 import org.apache.freemarker.test.util.FileTestCase;
 
 public class CanonicalFormTest extends FileTestCase {
@@ -54,13 +55,13 @@ public class CanonicalFormTest extends FileTestCase {
     
     private void assertCanonicalFormOf(String ftlFileName)
             throws IOException {
-        Configuration cfg = new Configuration(Configuration.VERSION_3_0_0);
-        cfg.setTemplateLoader(
-                new CopyrightCommentRemoverTemplateLoader(
-                        new ClassTemplateLoader(CanonicalFormTest.class, "")));
+        Configuration cfg = new TestConfigurationBuilder()
+                .templateLoader(
+                        new CopyrightCommentRemoverTemplateLoader(
+                                new ClassTemplateLoader(CanonicalFormTest.class, "")))
+                .build();
         StringWriter sw = new StringWriter();
         cfg.getTemplate(ftlFileName).dump(sw);
-
         assertExpectedFileEqualsString(ftlFileName + ".out", sw.toString());
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d61a45d/src/test/java/org/apache/freemarker/core/CoercionToTextualTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/CoercionToTextualTest.java b/src/test/java/org/apache/freemarker/core/CoercionToTextualTest.java
index 83f4ba5..c8a3335 100644
--- a/src/test/java/org/apache/freemarker/core/CoercionToTextualTest.java
+++ b/src/test/java/org/apache/freemarker/core/CoercionToTextualTest.java
@@ -29,6 +29,7 @@ import org.apache.freemarker.core.outputformat.impl.HTMLOutputFormat;
 import org.apache.freemarker.core.userpkg.HTMLISOTemplateDateFormatFactory;
 import org.apache.freemarker.core.userpkg.PrintfGTemplateNumberFormatFactory;
 import org.apache.freemarker.test.TemplateTest;
+import org.apache.freemarker.test.TestConfigurationBuilder;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -52,11 +53,11 @@ public class CoercionToTextualTest extends TemplateTest {
 
     @Test
     public void testEscBuiltin() throws IOException, TemplateException {
-        Configuration cfg = getConfiguration();
-        cfg.setOutputFormat(HTMLOutputFormat.INSTANCE);
-        cfg.setAutoEscapingPolicy(Configuration.DISABLE_AUTO_ESCAPING_POLICY);
-        cfg.setBooleanFormat("<y>,<n>");
-        
+        setConfiguration(createDefaultConfigurationBuilder()
+                .outputFormat(HTMLOutputFormat.INSTANCE)
+                .autoEscapingPolicy(ParsingConfiguration.DISABLE_AUTO_ESCAPING_POLICY)
+                .booleanFormat("<y>,<n>")
+                .build());
         assertOutput("${'a<b'?esc}", "a&lt;b");
         assertOutput("${n?string?esc}", "1.50E+03");
         assertOutput("${n?esc}", "1.50*10<sup>3</sup>");
@@ -117,16 +118,23 @@ public class CoercionToTextualTest extends TemplateTest {
         assertOutput("${'&' + b}", "&y");
         assertOutput("${'&' + m}", "&amp;<p>M</p>");
     }
-    
+
+    @Override
+    protected Configuration createDefaultConfiguration() throws Exception {
+        return createDefaultConfigurationBuilder().build();
+    }
+
+    private TestConfigurationBuilder createDefaultConfigurationBuilder() {
+        return new TestConfigurationBuilder()
+                .customNumberFormats(Collections.singletonMap("G", PrintfGTemplateNumberFormatFactory.INSTANCE))
+                .customDateFormats(Collections.singletonMap("HI", HTMLISOTemplateDateFormatFactory.INSTANCE))
+                .numberFormat("@G 3")
+                .dateTimeFormat("@HI")
+                .booleanFormat("y,n");
+    }
+
     @Before
     public void setup() throws TemplateModelException {
-        Configuration cfg = getConfiguration();
-        cfg.setCustomNumberFormats(Collections.singletonMap("G", PrintfGTemplateNumberFormatFactory.INSTANCE));
-        cfg.setCustomDateFormats(Collections.singletonMap("HI", HTMLISOTemplateDateFormatFactory.INSTANCE));
-        cfg.setNumberFormat("@G 3");
-        cfg.setDateTimeFormat("@HI");
-        cfg.setBooleanFormat("y,n");
-        
         addToDataModel("s", "abc");
         addToDataModel("n", 1500);
         addToDataModel("dt", TM);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d61a45d/src/test/java/org/apache/freemarker/core/ConfigurableTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/ConfigurableTest.java b/src/test/java/org/apache/freemarker/core/ConfigurableTest.java
index bff5dc0..ebcc465 100644
--- a/src/test/java/org/apache/freemarker/core/ConfigurableTest.java
+++ b/src/test/java/org/apache/freemarker/core/ConfigurableTest.java
@@ -96,7 +96,7 @@ public class ConfigurableTest {
             String fieldName = field.getName();
             if (fieldName.endsWith("_KEY")) {
                 String keyFieldValue = (String) field.get(null);
-                assertNotEquals(Configuration.CAMEL_CASE_NAMING_CONVENTION,
+                assertNotEquals(ParsingConfiguration.CAMEL_CASE_NAMING_CONVENTION,
                         _StringUtil.getIdentifierNamingConvention(keyFieldValue));
                 assertEquals(fieldName.substring(0, fieldName.length() - 4).toLowerCase(), keyFieldValue);
                 
@@ -109,7 +109,7 @@ public class ConfigurableTest {
                 
                 try {
                     String keyCCFieldValue = (String) confClass.getField(fieldName + "_CAMEL_CASE").get(null);
-                    assertNotEquals(Configuration.LEGACY_NAMING_CONVENTION,
+                    assertNotEquals(ParsingConfiguration.LEGACY_NAMING_CONVENTION,
                             _StringUtil.getIdentifierNamingConvention(keyCCFieldValue));
                     assertEquals(keyFieldValue, _StringUtil.camelCaseToUnderscored(keyCCFieldValue));
                 } catch (NoSuchFieldException e) {