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/07/04 07:44:17 UTC

[06/16] incubator-freemarker git commit: FREEMARKER-55: Add TaglibFactoryBuilder to reuse TaglibFactory creation logic

FREEMARKER-55: Add TaglibFactoryBuilder to reuse TaglibFactory creation logic


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

Branch: refs/heads/3
Commit: cb4fab3ae483d2fb645d296aff0ab94c78a8d4b9
Parents: 8e8c724
Author: Woonsan Ko <wo...@apache.org>
Authored: Thu Jun 29 23:30:47 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Thu Jun 29 23:30:47 2017 -0400

----------------------------------------------------------------------
 .../freemarker/servlet/FreemarkerServlet.java   | 170 +++++--------------
 .../servlet/HttpSessionHashModel.java           |  26 +--
 .../servlet/jsp/TaglibFactoryBuilder.java       | 149 ++++++++++++++++
 .../spring/web/view/FreemarkerView.java         | 112 +++++++++---
 .../spring/web/view/FreemarkerViewTest.java     |   1 -
 5 files changed, 295 insertions(+), 163 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cb4fab3a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java
----------------------------------------------------------------------
diff --git a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java
index cbd2fd7..923f9d4 100644
--- a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java
+++ b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java
@@ -27,9 +27,9 @@ import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Calendar;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.GregorianCalendar;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.regex.Pattern;
@@ -65,10 +65,8 @@ import org.apache.freemarker.core.templateresolver.impl.MultiTemplateLoader;
 import org.apache.freemarker.core.util._SecurityUtil;
 import org.apache.freemarker.core.util._StringUtil;
 import org.apache.freemarker.servlet.jsp.TaglibFactory;
-import org.apache.freemarker.servlet.jsp.TaglibFactory.ClasspathMetaInfTldSource;
-import org.apache.freemarker.servlet.jsp.TaglibFactory.ClearMetaInfTldSource;
 import org.apache.freemarker.servlet.jsp.TaglibFactory.MetaInfTldSource;
-import org.apache.freemarker.servlet.jsp.TaglibFactory.WebInfPerLibJarMetaInfTldSource;
+import org.apache.freemarker.servlet.jsp.TaglibFactoryBuilder;
 import org.slf4j.Logger;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
@@ -448,9 +446,9 @@ public class FreemarkerServlet extends HttpServlet {
 
     // Note these names start with dot, so they're essentially invisible from
     // a freemarker script.
-    private static final String ATTR_REQUEST_MODEL = ".freemarker.Request";
-    private static final String ATTR_REQUEST_PARAMETERS_MODEL = ".freemarker.RequestParameters";
-    private static final String ATTR_SESSION_MODEL = ".freemarker.Session";
+    public static final String ATTR_REQUEST_MODEL = ".freemarker.Request";
+    public static final String ATTR_REQUEST_PARAMETERS_MODEL = ".freemarker.RequestParameters";
+    public static final String ATTR_SESSION_MODEL = ".freemarker.Session";
     
     /** @deprecated We only keeps this attribute for backward compatibility, but actually aren't using it. */
     @Deprecated
@@ -642,7 +640,7 @@ public class FreemarkerServlet extends HttpServlet {
                 } else if (name.equals(INIT_PARAM_EXCEPTION_ON_MISSING_TEMPLATE)) {
                     exceptionOnMissingTemplate = _StringUtil.getYesNo(value);
                 } else if (name.equals(INIT_PARAM_META_INF_TLD_LOCATIONS)) {
-                    metaInfTldSources = parseAsMetaInfTldLocations(value);
+                    metaInfTldSources = TaglibFactoryBuilder.parseMetaInfTldLocations(InitParamParser.parseCommaSeparatedList(value));
                 } else if (name.equals(INIT_PARAM_CLASSPATH_TLDS)) {
                     List newClasspathTlds = new ArrayList();
                     if (classpathTlds != null) {
@@ -678,44 +676,6 @@ public class FreemarkerServlet extends HttpServlet {
         }
         LOG.debug("Using object wrapper {}", config.getObjectWrapper());
     }
-    
-    private List/*<MetaInfTldSource>*/ parseAsMetaInfTldLocations(String value) throws ParseException {
-        List/*<MetaInfTldSource>*/ metaInfTldSources = null;
-        
-        List/*<String>*/ values = InitParamParser.parseCommaSeparatedList(value);
-        for (Iterator it = values.iterator(); it.hasNext(); ) {
-            final String itemStr = (String) it.next();
-            final MetaInfTldSource metaInfTldSource;
-            if (itemStr.equals(META_INF_TLD_LOCATION_WEB_INF_PER_LIB_JARS)) {
-                metaInfTldSource = WebInfPerLibJarMetaInfTldSource.INSTANCE;
-            } else if (itemStr.startsWith(META_INF_TLD_LOCATION_CLASSPATH)) {
-                String itemRightSide = itemStr.substring(META_INF_TLD_LOCATION_CLASSPATH.length()).trim();
-                if (itemRightSide.length() == 0) {
-                    metaInfTldSource = new ClasspathMetaInfTldSource(Pattern.compile(".*", Pattern.DOTALL));
-                } else if (itemRightSide.startsWith(":")) {
-                    final String regexpStr = itemRightSide.substring(1).trim();
-                    if (regexpStr.length() == 0) {
-                        throw new ParseException("Empty regular expression after \""
-                                + META_INF_TLD_LOCATION_CLASSPATH + ":\"", -1);
-                    }
-                    metaInfTldSource = new ClasspathMetaInfTldSource(Pattern.compile(regexpStr));   
-                } else {
-                    throw new ParseException("Invalid \"" + META_INF_TLD_LOCATION_CLASSPATH
-                            + "\" value syntax: " + value, -1);
-                }
-            } else if (itemStr.startsWith(META_INF_TLD_LOCATION_CLEAR)) {
-                metaInfTldSource = ClearMetaInfTldSource.INSTANCE;
-            } else {
-                throw new ParseException("Item has no recognized source type prefix: " + itemStr, -1);
-            }
-            if (metaInfTldSources == null) {
-                metaInfTldSources = new ArrayList();
-            }
-            metaInfTldSources.add(metaInfTldSource);
-        }
-        
-        return metaInfTldSources;
-    }
 
     /**
      * Create the template loader. The default implementation will create a {@link ClassTemplateLoader} if the template
@@ -1000,8 +960,8 @@ public class FreemarkerServlet extends HttpServlet {
                 sessionModel = (HttpSessionHashModel) session.getAttribute(ATTR_SESSION_MODEL);
                 if (sessionModel == null || sessionModel.isOrphaned(session)) {
                     sessionModel = new HttpSessionHashModel(session, objectWrapper);
-                    initializeSessionAndInstallModel(request, response, 
-                            sessionModel, session);
+                    session.setAttribute(ATTR_SESSION_MODEL, sessionModel);
+                    initializeSession(request, response);
                 }
             } else {
                 sessionModel = new HttpSessionHashModel(this, request, response, objectWrapper);
@@ -1040,73 +1000,47 @@ public class FreemarkerServlet extends HttpServlet {
      * 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.
      */
-    protected TaglibFactory createTaglibFactory(ObjectWrapper objectWrapper,
-            ServletContext servletContext) throws TemplateModelException {
-        TaglibFactory taglibFactory = new TaglibFactory(servletContext);
-        
-        taglibFactory.setObjectWrapper(objectWrapper);
-        
-        {
-            List/*<MetaInfTldSource>*/ mergedMetaInfTldSources = new ArrayList();
+    @SuppressWarnings("unchecked")
+    protected TaglibFactory createTaglibFactory(ObjectWrapper objectWrapper, ServletContext servletContext)
+            throws TemplateModelException {
 
-            if (metaInfTldSources != null) {
-                mergedMetaInfTldSources.addAll(metaInfTldSources);
-            }
-            
-            String sysPropVal = _SecurityUtil.getSystemProperty(SYSTEM_PROPERTY_META_INF_TLD_SOURCES, null);
-            if (sysPropVal != null) {
-                try {
-                    List metaInfTldSourcesSysProp = parseAsMetaInfTldLocations(sysPropVal);
-                    if (metaInfTldSourcesSysProp != null) {
-                        mergedMetaInfTldSources.addAll(metaInfTldSourcesSysProp);
-                    }
-                } catch (ParseException e) {
-                    throw new TemplateModelException("Failed to parse system property \""
-                            + SYSTEM_PROPERTY_META_INF_TLD_SOURCES + "\"", e);
-                }
-            }
+        List<MetaInfTldSource> metaInfTldSourcesFromSysProp = null;
+        try {
+            final String prop = _SecurityUtil.getSystemProperty(SYSTEM_PROPERTY_META_INF_TLD_SOURCES, null);
+            metaInfTldSourcesFromSysProp = (List<MetaInfTldSource>) ((prop != null)
+                    ? TaglibFactoryBuilder.parseMetaInfTldLocations(InitParamParser.parseCommaSeparatedList(prop))
+                    : Collections.emptyList());
+        } catch (ParseException e) {
+            throw new TemplateModelException(
+                    "Failed to parse system property \"" + SYSTEM_PROPERTY_META_INF_TLD_SOURCES + "\"", e);
+        }
 
-            List/*<Pattern>*/ jettyTaglibJarPatterns = null;
-            try {
-                final String attrVal = (String) servletContext.getAttribute(ATTR_JETTY_CP_TAGLIB_JAR_PATTERNS);
-                jettyTaglibJarPatterns = attrVal != null ? InitParamParser.parseCommaSeparatedPatterns(attrVal) : null;
-            } catch (Exception e) {
-                LOG.error("Failed to parse application context attribute \""
-                        + ATTR_JETTY_CP_TAGLIB_JAR_PATTERNS + "\" - it will be ignored", e);
-            }
-            if (jettyTaglibJarPatterns != null) {
-                for (Iterator/*<Pattern>*/ it = jettyTaglibJarPatterns.iterator(); it.hasNext(); ) {
-                    Pattern pattern = (Pattern) it.next();
-                    mergedMetaInfTldSources.add(new ClasspathMetaInfTldSource(pattern));
-                }
-            }
-            
-            taglibFactory.setMetaInfTldSources(mergedMetaInfTldSources);
+        List<Pattern> jettyTaglibJarPatterns = null;
+        try {
+            final String attrVal = (String) servletContext.getAttribute(ATTR_JETTY_CP_TAGLIB_JAR_PATTERNS);
+            jettyTaglibJarPatterns = (attrVal != null) ? InitParamParser.parseCommaSeparatedPatterns(attrVal)
+                    : Collections.emptyList();
+        } catch (Exception e) {
+            LOG.error("Failed to parse application context attribute \"" + ATTR_JETTY_CP_TAGLIB_JAR_PATTERNS
+                    + "\" - it will be ignored", e);
         }
-        
-        {
-            List/*<String>*/ mergedClassPathTlds = new ArrayList();
-            if (classpathTlds != null) {
-                mergedClassPathTlds.addAll(classpathTlds);
-            }
-            
-            String sysPropVal = _SecurityUtil.getSystemProperty(SYSTEM_PROPERTY_CLASSPATH_TLDS, null);
-            if (sysPropVal != null) {
-                try {
-                    List/*<String>*/ classpathTldsSysProp = InitParamParser.parseCommaSeparatedList(sysPropVal);
-                    if (classpathTldsSysProp != null) {
-                        mergedClassPathTlds.addAll(classpathTldsSysProp);
-                    }
-                } catch (ParseException e) {
-                    throw new TemplateModelException("Failed to parse system property \""
-                            + SYSTEM_PROPERTY_CLASSPATH_TLDS + "\"", e);
-                }
-            }
-            
-            taglibFactory.setClasspathTlds(mergedClassPathTlds);
+
+        List<String> classpathTldsFromSysProp = null;
+        try {
+            final String prop = _SecurityUtil.getSystemProperty(SYSTEM_PROPERTY_CLASSPATH_TLDS, null);
+            classpathTldsFromSysProp = (prop != null) ? InitParamParser.parseCommaSeparatedList(prop)
+                    : Collections.emptyList();
+        } catch (ParseException e) {
+            throw new TemplateModelException(
+                    "Failed to parse system property \"" + SYSTEM_PROPERTY_CLASSPATH_TLDS + "\"", e);
         }
-        
-        return taglibFactory;        
+
+        return new TaglibFactoryBuilder(servletContext, objectWrapper)
+                .addAllMetaInfTldSources(metaInfTldSources)
+                .addAllMetaInfTldSources(metaInfTldSourcesFromSysProp)
+                .addAllJettyMetaInfTldJarPatterns(jettyTaglibJarPatterns)
+                .addAllClasspathTlds(classpathTlds)
+                .addAllClasspathTlds(classpathTldsFromSysProp).build();
     }
 
     /**
@@ -1134,14 +1068,6 @@ public class FreemarkerServlet extends HttpServlet {
     protected List/*<MetaInfTldSource>*/ createDefaultMetaInfTldSources() {
         return TaglibFactory.DEFAULT_META_INF_TLD_SOURCES;
     }
-    
-    void initializeSessionAndInstallModel(HttpServletRequest request,
-            HttpServletResponse response, HttpSessionHashModel sessionModel, 
-            HttpSession session)
-            throws ServletException, IOException {
-        session.setAttribute(ATTR_SESSION_MODEL, sessionModel);
-        initializeSession(request, response);
-    }
 
     /**
      * Maps the request URL to a template path (template name) that is passed to
@@ -1279,10 +1205,8 @@ public class FreemarkerServlet extends HttpServlet {
      * @param request the actual HTTP request
      * @param response the actual HTTP response
      */
-    protected void initializeSession(
-        HttpServletRequest request,
-        HttpServletResponse response)
-        throws ServletException, IOException {
+    protected void initializeSession(HttpServletRequest request, HttpServletResponse response)
+            throws ServletException, IOException {
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cb4fab3a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/HttpSessionHashModel.java
----------------------------------------------------------------------
diff --git a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/HttpSessionHashModel.java b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/HttpSessionHashModel.java
index 216fd94..469dc29 100644
--- a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/HttpSessionHashModel.java
+++ b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/HttpSessionHashModel.java
@@ -21,6 +21,7 @@ package org.apache.freemarker.servlet;
 
 import java.io.Serializable;
 
+import javax.servlet.GenericServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
@@ -40,7 +41,7 @@ public final class HttpSessionHashModel implements TemplateHashModel, Serializab
     private transient final ObjectWrapper wrapper;
 
     // These are required for lazy initializing session
-    private transient final FreemarkerServlet servlet;
+    private transient final GenericServlet servlet;
     private transient final HttpServletRequest request;
     private transient final HttpServletResponse response;
     
@@ -62,16 +63,15 @@ public final class HttpSessionHashModel implements TemplateHashModel, Serializab
      * Use this constructor when the session isn't already created. It is passed
      * enough parameters so that the session can be properly initialized after
      * it's detected that it was created.
-     * @param servlet the FreemarkerServlet that created this model. If the
-     * model is not created through FreemarkerServlet, leave this argument as
-     * null.
+     * @param servlet the servlet (e.g, FreemarkerServlet) that created this model. If the
+     * model is not created through FreemarkerServlet, this argument can be left as null.
      * @param request the actual request
      * @param response the actual response
      * @param wrapper an object wrapper used to wrap session attributes
      */
-    public HttpSessionHashModel(FreemarkerServlet servlet, HttpServletRequest request, HttpServletResponse response, ObjectWrapper wrapper) {
+    public HttpSessionHashModel(GenericServlet servlet, HttpServletRequest request, HttpServletResponse response, ObjectWrapper wrapper) {
         this.wrapper = wrapper;
-        
+
         this.servlet = servlet;
         this.request = request;
         this.response = response;
@@ -88,8 +88,11 @@ public final class HttpSessionHashModel implements TemplateHashModel, Serializab
             session = request.getSession(false);
             if (session != null && servlet != null) {
                 try {
-                    servlet.initializeSessionAndInstallModel(request, response, 
-                            this, session);
+                    session.setAttribute(FreemarkerServlet.ATTR_SESSION_MODEL, this);
+
+                    if (servlet instanceof FreemarkerServlet) {
+                        ((FreemarkerServlet) servlet).initializeSession(request, response);
+                    }
                 } catch (RuntimeException e) {
                     throw e;
                 } catch (Exception e) {
@@ -99,14 +102,13 @@ public final class HttpSessionHashModel implements TemplateHashModel, Serializab
         }
     }
 
-    boolean isOrphaned(HttpSession currentSession) {
+    public boolean isOrphaned(HttpSession currentSession) {
         return (session != null && session != currentSession) || 
             (session == null && request == null);
     }
-    
+
     @Override
-    public boolean isEmpty()
-    throws TemplateModelException {
+    public boolean isEmpty() throws TemplateModelException {
         checkSessionExistence();
         return session == null || !session.getAttributeNames().hasMoreElements();
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cb4fab3a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactoryBuilder.java
----------------------------------------------------------------------
diff --git a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactoryBuilder.java b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactoryBuilder.java
new file mode 100644
index 0000000..6d4405d
--- /dev/null
+++ b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactoryBuilder.java
@@ -0,0 +1,149 @@
+/*
+ * 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.jsp;
+
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import javax.servlet.ServletContext;
+
+import org.apache.freemarker.core.model.ObjectWrapper;
+import org.apache.freemarker.servlet.FreemarkerServlet;
+import org.apache.freemarker.servlet.jsp.TaglibFactory.ClasspathMetaInfTldSource;
+import org.apache.freemarker.servlet.jsp.TaglibFactory.ClearMetaInfTldSource;
+import org.apache.freemarker.servlet.jsp.TaglibFactory.MetaInfTldSource;
+import org.apache.freemarker.servlet.jsp.TaglibFactory.WebInfPerLibJarMetaInfTldSource;
+
+public class TaglibFactoryBuilder {
+
+    private final ServletContext servletContext;
+    private final ObjectWrapper objectWrapper;
+    private List<MetaInfTldSource> metaInfTldSources = new ArrayList<>();
+    private List<String> classPathTlds = new ArrayList<>();
+
+    public TaglibFactoryBuilder(ServletContext servletContext, ObjectWrapper objectWrapper) {
+        this.servletContext = servletContext;
+        this.objectWrapper = objectWrapper;
+    }
+
+    public TaglibFactoryBuilder addMetaInfTldSource(MetaInfTldSource metaInfTldSource) {
+        metaInfTldSources.add(metaInfTldSource);
+        return this;
+    }
+
+    public TaglibFactoryBuilder addAllMetaInfTldSources(List<MetaInfTldSource> metaInfTldSources) {
+        this.metaInfTldSources.addAll(metaInfTldSources);
+        return this;
+    }
+
+    public TaglibFactoryBuilder addMetaInfTldLocation(String metaInfTldLocation) throws ParseException {
+        return addMetaInfTldSource(parseMetaInfTldLocation(metaInfTldLocation));
+    }
+
+    public TaglibFactoryBuilder addMetaInfTldLocations(List<String> metaInfTldLocations) throws ParseException {
+        return addAllMetaInfTldSources(parseMetaInfTldLocations(metaInfTldLocations));
+    }
+
+    public TaglibFactoryBuilder addJettyMetaInfTldJarPattern(Pattern pattern) {
+        return addMetaInfTldSource(new ClasspathMetaInfTldSource(pattern));
+    }
+
+    public TaglibFactoryBuilder addAllJettyMetaInfTldJarPatterns(List<Pattern> patterns) {
+        for (Pattern pattern : patterns) {
+            addJettyMetaInfTldJarPattern(pattern);
+        }
+
+        return this;
+    }
+
+    public TaglibFactoryBuilder addClasspathTld(String classpathTld) {
+        classPathTlds.add(classpathTld);
+        return this;
+    }
+
+    public TaglibFactoryBuilder addAllClasspathTlds(List<String> classpathTlds) {
+        classPathTlds.addAll(classpathTlds);
+        return this;
+    }
+
+    public TaglibFactory build() {
+        TaglibFactory taglibFactory = new TaglibFactory(servletContext);
+        taglibFactory.setObjectWrapper(objectWrapper);
+        taglibFactory.setMetaInfTldSources(metaInfTldSources);
+        taglibFactory.setClasspathTlds(classPathTlds);
+        return taglibFactory;
+    }
+
+    public static MetaInfTldSource parseMetaInfTldLocation(String value) throws ParseException {
+        MetaInfTldSource metaInfTldSource;
+
+        if (value.equals(FreemarkerServlet.META_INF_TLD_LOCATION_WEB_INF_PER_LIB_JARS)) {
+            metaInfTldSource = WebInfPerLibJarMetaInfTldSource.INSTANCE;
+        } else if (value.startsWith(FreemarkerServlet.META_INF_TLD_LOCATION_CLASSPATH)) {
+            String itemRightSide = value.substring(FreemarkerServlet.META_INF_TLD_LOCATION_CLASSPATH.length())
+                    .trim();
+
+            if (itemRightSide.length() == 0) {
+                metaInfTldSource = new ClasspathMetaInfTldSource(Pattern.compile(".*", Pattern.DOTALL));
+            } else if (itemRightSide.startsWith(":")) {
+                final String regexpStr = itemRightSide.substring(1).trim();
+                if (regexpStr.length() == 0) {
+                    throw new ParseException("Empty regular expression after \""
+                            + FreemarkerServlet.META_INF_TLD_LOCATION_CLASSPATH + ":\"", -1);
+                }
+                metaInfTldSource = new ClasspathMetaInfTldSource(Pattern.compile(regexpStr));
+            } else {
+                throw new ParseException("Invalid \"" + FreemarkerServlet.META_INF_TLD_LOCATION_CLASSPATH
+                        + "\" value syntax: " + value, -1);
+            }
+        } else if (value.startsWith(FreemarkerServlet.META_INF_TLD_LOCATION_CLEAR)) {
+            metaInfTldSource = ClearMetaInfTldSource.INSTANCE;
+        } else {
+            throw new ParseException("Item has no recognized source type prefix: " + value, -1);
+        }
+
+        return metaInfTldSource;
+    }
+
+    public static List<MetaInfTldSource> parseMetaInfTldLocations(List<String> values) throws ParseException {
+        List<MetaInfTldSource> metaInfTldSources = null;
+
+        if (values != null) {
+            for (String value : values) {
+                final MetaInfTldSource metaInfTldSource = parseMetaInfTldLocation(value);
+
+                if (metaInfTldSources == null) {
+                    metaInfTldSources = new ArrayList();
+                }
+
+                metaInfTldSources.add(metaInfTldSource);
+            }
+        }
+
+        if (metaInfTldSources == null) {
+            metaInfTldSources = Collections.emptyList();
+        }
+
+        return metaInfTldSources;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cb4fab3a/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreemarkerView.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreemarkerView.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreemarkerView.java
index 934a458..64b3158 100644
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreemarkerView.java
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreemarkerView.java
@@ -31,6 +31,7 @@ import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
 
 import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
 import org.apache.freemarker.core.model.TemplateHashModel;
@@ -41,31 +42,38 @@ import org.apache.freemarker.servlet.HttpRequestParametersHashModel;
 import org.apache.freemarker.servlet.HttpSessionHashModel;
 import org.apache.freemarker.servlet.ServletContextHashModel;
 import org.apache.freemarker.servlet.jsp.TaglibFactory;
+import org.apache.freemarker.servlet.jsp.TaglibFactoryBuilder;
 
 public class FreemarkerView extends AbstractFreemarkerView {
 
-    private PageContextServletConfig pageContextServletConfig;
+    private volatile PageContextServlet pageContextServlet;
 
-    private PageContextServlet pageContextServlet;
+    private volatile ServletContextHashModel servletContextModel;
 
-    private ServletContextHashModel servletContextModel;
-
-    private TaglibFactory taglibFactory;
+    private volatile TaglibFactory taglibFactory;
 
     public PageContextServlet getPageContextServlet() {
-        // TODO: proper locking...
-        if (pageContextServlet == null) {
-            pageContextServlet = new PageContextServlet();
-            pageContextServletConfig = new PageContextServletConfig(getServletContext(), getBeanName());
-
-            try {
-                pageContextServlet.init(pageContextServletConfig);
-            } catch (ServletException e) {
-                // never happen
+        PageContextServlet servlet = pageContextServlet;
+
+        if (servlet == null) {
+            synchronized (this) {
+                servlet = pageContextServlet;
+
+                if (servlet == null) {
+                    servlet = new PageContextServlet();
+
+                    try {
+                        servlet.init(new PageContextServletConfig(getServletContext(), getBeanName()));
+                    } catch (ServletException e) {
+                        // never happens...
+                    }
+
+                    pageContextServlet = servlet;
+                }
             }
         }
 
-        return pageContextServlet;
+        return servlet;
     }
 
     public void setPageContextServlet(PageContextServlet pageContextServlet) {
@@ -73,12 +81,20 @@ public class FreemarkerView extends AbstractFreemarkerView {
     }
 
     public ServletContextHashModel getServletContextModel() {
-        // TODO: proper locking...
-        if (servletContextModel == null) {
-            servletContextModel = new ServletContextHashModel(getPageContextServlet(), getObjectWrapperForModel());
+        ServletContextHashModel contextModel = servletContextModel;
+
+        if (contextModel == null) {
+            synchronized (this) {
+                contextModel = servletContextModel;
+
+                if (contextModel == null) {
+                    contextModel = new ServletContextHashModel(getPageContextServlet(), getObjectWrapperForModel());
+                    servletContextModel = contextModel;
+                }
+            }
         }
 
-        return servletContextModel;
+        return contextModel;
     }
 
     public void setServletContextModel(ServletContextHashModel servletContextModel) {
@@ -86,8 +102,22 @@ public class FreemarkerView extends AbstractFreemarkerView {
     }
 
     public TaglibFactory getTaglibFactory() {
-        // TODO
-        return taglibFactory;
+        TaglibFactory tlFactory = taglibFactory;
+
+        if (tlFactory == null) {
+            synchronized (this) {
+                tlFactory = taglibFactory;
+
+                if (tlFactory == null) {
+                    tlFactory = new TaglibFactoryBuilder(getServletContext(), getObjectWrapperForModel())
+                            .build();
+
+                    taglibFactory = tlFactory;
+                }
+            }
+        }
+
+        return tlFactory;
     }
 
     public void setTaglibFactory(TaglibFactory taglibFactory) {
@@ -97,23 +127,51 @@ public class FreemarkerView extends AbstractFreemarkerView {
     @Override
     protected TemplateHashModel createModel(Map<String, Object> map, ObjectWrapperAndUnwrapper objectWrapperForModel,
             HttpServletRequest request, HttpServletResponse response) {
+
         AllHttpScopesHashModel model = new AllHttpScopesHashModel(objectWrapperForModel, getServletContext(), request);
+
         model.putUnlistedModel(FreemarkerServlet.KEY_APPLICATION, getServletContextModel());
+
         model.putUnlistedModel(FreemarkerServlet.KEY_SESSION,
                 getHttpSessionModel(objectWrapperForModel, request, response));
-        model.putUnlistedModel(FreemarkerServlet.KEY_REQUEST,
-                new HttpRequestHashModel(request, response, objectWrapperForModel));
-        model.putUnlistedModel(FreemarkerServlet.KEY_REQUEST_PARAMETERS,
-                new HttpRequestParametersHashModel(request, objectWrapperForModel));
+
+        HttpRequestHashModel requestModel = (HttpRequestHashModel) request
+                .getAttribute(FreemarkerServlet.ATTR_REQUEST_MODEL);
+        HttpRequestParametersHashModel requestParametersModel = (HttpRequestParametersHashModel) request
+                .getAttribute(FreemarkerServlet.ATTR_REQUEST_PARAMETERS_MODEL);
+
+        if (requestModel == null || requestModel.getRequest() != request) {
+            requestModel = new HttpRequestHashModel(request, response, objectWrapperForModel);
+            request.setAttribute(FreemarkerServlet.ATTR_REQUEST_MODEL, requestModel);
+            requestParametersModel = new HttpRequestParametersHashModel(request, objectWrapperForModel);
+        }
+
+        model.putUnlistedModel(FreemarkerServlet.KEY_REQUEST, requestModel);
+        model.putUnlistedModel(FreemarkerServlet.KEY_REQUEST_PARAMETERS, requestParametersModel);
+
         model.putUnlistedModel(FreemarkerServlet.KEY_JSP_TAGLIBS, getTaglibFactory());
+
         model.putAll(map);
+
         return model;
     }
 
     protected HttpSessionHashModel getHttpSessionModel(ObjectWrapperAndUnwrapper objectWrapperForModel,
             HttpServletRequest request, HttpServletResponse response) {
-        // TODO
-        HttpSessionHashModel sessionModel = new HttpSessionHashModel(null, request, response, objectWrapperForModel);
+        HttpSessionHashModel sessionModel;
+        HttpSession session = request.getSession(false);
+
+        if (session != null) {
+            sessionModel = (HttpSessionHashModel) session.getAttribute(FreemarkerServlet.ATTR_SESSION_MODEL);
+
+            if (sessionModel == null || sessionModel.isOrphaned(session)) {
+                sessionModel = new HttpSessionHashModel(session, objectWrapperForModel);
+                session.setAttribute(FreemarkerServlet.ATTR_SESSION_MODEL, sessionModel);
+            }
+        } else {
+            sessionModel = new HttpSessionHashModel(getPageContextServlet(), request, response, objectWrapperForModel);
+        }
+
         return sessionModel;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cb4fab3a/freemarker-spring/src/test/java/org/apache/freemarker/spring/web/view/FreemarkerViewTest.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/web/view/FreemarkerViewTest.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/web/view/FreemarkerViewTest.java
index 4f21e58..80fc2b3 100644
--- a/freemarker-spring/src/test/java/org/apache/freemarker/spring/web/view/FreemarkerViewTest.java
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/web/view/FreemarkerViewTest.java
@@ -95,7 +95,6 @@ public class FreemarkerViewTest {
         request.setSession(session);
         request.setAttribute("promotion", "Fresh blue berries");
 
-        // TODO: Add 'Application.attributeName' example, too.
         templateLoader.putTemplate("default-model.ftl",
                 "${name!}, you have ${Session.itemCountInCart!0} items in cart. " + "Hot deal: ${Request.promotion}. "
                         + "BTW, you're ${Application.visitorCount}th visitor. "