This is an automated email from the ASF dual-hosted git repository. ddekany pushed a commit to branch 2.3-gae in repository https://gitbox.apache.org/repos/asf/freemarker.git
commit 3b0de7223e110bc02b6728699eab662bdb8c5b67 Author: ddekany <[email protected]> AuthorDate: Thu Apr 11 10:19:08 2024 +0200 Fixed: Configuration.setServletContextForTemplateLoading did not support Jakarta --- .../java/freemarker/template/Configuration.java | 87 ++++++++++++++++------ .../SetServletContextForTemplateLoadingTest.java | 51 +++++++++++++ 2 files changed, 117 insertions(+), 21 deletions(-) diff --git a/freemarker-core/src/main/java/freemarker/template/Configuration.java b/freemarker-core/src/main/java/freemarker/template/Configuration.java index d08f815f..19488049 100644 --- a/freemarker-core/src/main/java/freemarker/template/Configuration.java +++ b/freemarker-core/src/main/java/freemarker/template/Configuration.java @@ -1636,36 +1636,81 @@ public class Configuration extends Configurable implements Cloneable, ParserConf * * @param servletContext the {@code javax.servlet.ServletContext} object. (The declared type is {@link Object} * to prevent class loading error when using FreeMarker in an environment where - * there's no servlet classes available.) - * @param path the path relative to the ServletContext. + * there's no servlet classes available.) Can't be {@code null}. + * @param path the path relative to the ServletContext; maybe {@code null}. * * @see #setTemplateLoader(TemplateLoader) */ public void setServletContextForTemplateLoading(Object servletContext, String path) { + NullArgumentException.check("servletContext", servletContext); + + // To not introduce linking-time dependency on servlets, we load all related classes on runtime. + Class<?> servletContextClass = null; + Boolean jakartaMode = null; + + Exception jakartaServletClassLoadingException = null; try { - // Don't introduce linking-time dependency on servlets - final Class webappTemplateLoaderClass = ClassUtil.forName("freemarker.cache.WebappTemplateLoader"); - - // Don't introduce linking-time dependency on servlets - final Class servletContextClass = ClassUtil.forName("javax.servlet.ServletContext"); - - final Class[] constructorParamTypes; - final Object[] constructorParams; - if (path == null) { - constructorParamTypes = new Class[] { servletContextClass }; - constructorParams = new Object[] { servletContext }; - } else { - constructorParamTypes = new Class[] { servletContextClass, String.class }; - constructorParams = new Object[] { servletContext, path }; + servletContextClass = ClassUtil.forName("jakarta.servlet.ServletContext"); + if (servletContextClass.isInstance(servletContext)) { + jakartaMode = true; } - - setTemplateLoader( (TemplateLoader) - webappTemplateLoaderClass + } catch (Exception e) { + jakartaServletClassLoadingException = e; + } + + Exception javaxServletClassLoadingException = null; + if (jakartaMode == null) { + try { + servletContextClass = ClassUtil.forName("javax.servlet.ServletContext"); + if (servletContextClass.isInstance(servletContext)) { + jakartaMode = false; + } + } catch (Exception e) { + javaxServletClassLoadingException = e; + } + } + + if (servletContextClass == null) { + throw new UnsupportedOperationException("Failed to get ServletContext class; probably Servlet API-s " + + "are not supported in this environment, but check the exceptions:" + + "\n- When attempted use Jakarta Servlet support: " + jakartaServletClassLoadingException + + "\n- When attempted use javax Servlet support: " + javaxServletClassLoadingException); + } + if (jakartaMode == null) { + throw new IllegalArgumentException("servletContext must implement ServletContext, but " + + servletContext.getClass().getName() + " does not."); + } + + Class<?> webappTemplateLoaderClass; + try { + webappTemplateLoaderClass = ClassUtil.forName( + jakartaMode + ? "freemarker.ext.jakarta.servlet.WebappTemplateLoader" + : "freemarker.cache.WebappTemplateLoader"); + } catch (ClassNotFoundException e) { + throw new RuntimeException("Failed to get WebappTemplateLoader class", e); + } + + final Class<?>[] constructorParamTypes; + final Object[] constructorParams; + if (path == null) { + constructorParamTypes = new Class<?>[] { servletContextClass }; + constructorParams = new Object[] { servletContext }; + } else { + constructorParamTypes = new Class[] { servletContextClass, String.class }; + constructorParams = new Object[] { servletContext, path }; + } + + TemplateLoader templateLoader; + try { + templateLoader = (TemplateLoader) webappTemplateLoaderClass .getConstructor(constructorParamTypes) - .newInstance(constructorParams)); + .newInstance(constructorParams); } catch (Exception e) { - throw new BugException(e); + throw new RuntimeException("Failed to instantiate " + webappTemplateLoaderClass.getName(), e); } + + setTemplateLoader(templateLoader); } /** diff --git a/freemarker-javax-servlet/src/test/java/freemarker/template/SetServletContextForTemplateLoadingTest.java b/freemarker-javax-servlet/src/test/java/freemarker/template/SetServletContextForTemplateLoadingTest.java new file mode 100644 index 00000000..4e96cb6d --- /dev/null +++ b/freemarker-javax-servlet/src/test/java/freemarker/template/SetServletContextForTemplateLoadingTest.java @@ -0,0 +1,51 @@ +/* + * 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 freemarker.template; + +import static org.junit.Assert.*; + +import javax.servlet.ServletContext; + +import org.hamcrest.Matchers; +import org.junit.Test; + +import freemarker.cache.WebappTemplateLoader; + +public class SetServletContextForTemplateLoadingTest { + @Test + public void testSuccess() { + Configuration cfg = new Configuration(Configuration.VERSION_2_3_33); + ServletContext servletContext = new MockServletContext(); + cfg.setServletContextForTemplateLoading(servletContext, "foo"); + cfg.setServletContextForTemplateLoading(servletContext, null); + assertThat(cfg.getTemplateLoader(), Matchers.instanceOf(WebappTemplateLoader.class)); + } + + @Test + public void testIllegalArgument() { + Configuration cfg = new Configuration(Configuration.VERSION_2_3_33); + try { + cfg.setServletContextForTemplateLoading("bad type", "foo"); + fail(); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), Matchers.containsString("ServletContext")); + } + } +}
