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 <[email protected]> Authored: Thu Jun 29 23:30:47 2017 -0400 Committer: Woonsan Ko <[email protected]> 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. "
