Skip to content

Commit 3b0de72

Browse files
committed
Fixed: Configuration.setServletContextForTemplateLoading did not support Jakarta
1 parent be9849a commit 3b0de72

2 files changed

Lines changed: 117 additions & 21 deletions

File tree

freemarker-core/src/main/java/freemarker/template/Configuration.java

Lines changed: 66 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1636,36 +1636,81 @@ public void setDirectoryForTemplateLoading(File dir) throws IOException {
16361636
*
16371637
* @param servletContext the {@code javax.servlet.ServletContext} object. (The declared type is {@link Object}
16381638
* to prevent class loading error when using FreeMarker in an environment where
1639-
* there's no servlet classes available.)
1640-
* @param path the path relative to the ServletContext.
1639+
* there's no servlet classes available.) Can't be {@code null}.
1640+
* @param path the path relative to the ServletContext; maybe {@code null}.
16411641
*
16421642
* @see #setTemplateLoader(TemplateLoader)
16431643
*/
16441644
public void setServletContextForTemplateLoading(Object servletContext, String path) {
1645+
NullArgumentException.check("servletContext", servletContext);
1646+
1647+
// To not introduce linking-time dependency on servlets, we load all related classes on runtime.
1648+
Class<?> servletContextClass = null;
1649+
Boolean jakartaMode = null;
1650+
1651+
Exception jakartaServletClassLoadingException = null;
16451652
try {
1646-
// Don't introduce linking-time dependency on servlets
1647-
final Class webappTemplateLoaderClass = ClassUtil.forName("freemarker.cache.WebappTemplateLoader");
1648-
1649-
// Don't introduce linking-time dependency on servlets
1650-
final Class servletContextClass = ClassUtil.forName("javax.servlet.ServletContext");
1651-
1652-
final Class[] constructorParamTypes;
1653-
final Object[] constructorParams;
1654-
if (path == null) {
1655-
constructorParamTypes = new Class[] { servletContextClass };
1656-
constructorParams = new Object[] { servletContext };
1657-
} else {
1658-
constructorParamTypes = new Class[] { servletContextClass, String.class };
1659-
constructorParams = new Object[] { servletContext, path };
1653+
servletContextClass = ClassUtil.forName("jakarta.servlet.ServletContext");
1654+
if (servletContextClass.isInstance(servletContext)) {
1655+
jakartaMode = true;
16601656
}
1661-
1662-
setTemplateLoader( (TemplateLoader)
1663-
webappTemplateLoaderClass
1657+
} catch (Exception e) {
1658+
jakartaServletClassLoadingException = e;
1659+
}
1660+
1661+
Exception javaxServletClassLoadingException = null;
1662+
if (jakartaMode == null) {
1663+
try {
1664+
servletContextClass = ClassUtil.forName("javax.servlet.ServletContext");
1665+
if (servletContextClass.isInstance(servletContext)) {
1666+
jakartaMode = false;
1667+
}
1668+
} catch (Exception e) {
1669+
javaxServletClassLoadingException = e;
1670+
}
1671+
}
1672+
1673+
if (servletContextClass == null) {
1674+
throw new UnsupportedOperationException("Failed to get ServletContext class; probably Servlet API-s "
1675+
+ "are not supported in this environment, but check the exceptions:"
1676+
+ "\n- When attempted use Jakarta Servlet support: " + jakartaServletClassLoadingException
1677+
+ "\n- When attempted use javax Servlet support: " + javaxServletClassLoadingException);
1678+
}
1679+
if (jakartaMode == null) {
1680+
throw new IllegalArgumentException("servletContext must implement ServletContext, but "
1681+
+ servletContext.getClass().getName() + " does not.");
1682+
}
1683+
1684+
Class<?> webappTemplateLoaderClass;
1685+
try {
1686+
webappTemplateLoaderClass = ClassUtil.forName(
1687+
jakartaMode
1688+
? "freemarker.ext.jakarta.servlet.WebappTemplateLoader"
1689+
: "freemarker.cache.WebappTemplateLoader");
1690+
} catch (ClassNotFoundException e) {
1691+
throw new RuntimeException("Failed to get WebappTemplateLoader class", e);
1692+
}
1693+
1694+
final Class<?>[] constructorParamTypes;
1695+
final Object[] constructorParams;
1696+
if (path == null) {
1697+
constructorParamTypes = new Class<?>[] { servletContextClass };
1698+
constructorParams = new Object[] { servletContext };
1699+
} else {
1700+
constructorParamTypes = new Class[] { servletContextClass, String.class };
1701+
constructorParams = new Object[] { servletContext, path };
1702+
}
1703+
1704+
TemplateLoader templateLoader;
1705+
try {
1706+
templateLoader = (TemplateLoader) webappTemplateLoaderClass
16641707
.getConstructor(constructorParamTypes)
1665-
.newInstance(constructorParams));
1708+
.newInstance(constructorParams);
16661709
} catch (Exception e) {
1667-
throw new BugException(e);
1710+
throw new RuntimeException("Failed to instantiate " + webappTemplateLoaderClass.getName(), e);
16681711
}
1712+
1713+
setTemplateLoader(templateLoader);
16691714
}
16701715

16711716
/**
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package freemarker.template;
21+
22+
import static org.junit.Assert.*;
23+
24+
import javax.servlet.ServletContext;
25+
26+
import org.hamcrest.Matchers;
27+
import org.junit.Test;
28+
29+
import freemarker.cache.WebappTemplateLoader;
30+
31+
public class SetServletContextForTemplateLoadingTest {
32+
@Test
33+
public void testSuccess() {
34+
Configuration cfg = new Configuration(Configuration.VERSION_2_3_33);
35+
ServletContext servletContext = new MockServletContext();
36+
cfg.setServletContextForTemplateLoading(servletContext, "foo");
37+
cfg.setServletContextForTemplateLoading(servletContext, null);
38+
assertThat(cfg.getTemplateLoader(), Matchers.instanceOf(WebappTemplateLoader.class));
39+
}
40+
41+
@Test
42+
public void testIllegalArgument() {
43+
Configuration cfg = new Configuration(Configuration.VERSION_2_3_33);
44+
try {
45+
cfg.setServletContextForTemplateLoading("bad type", "foo");
46+
fail();
47+
} catch (IllegalArgumentException e) {
48+
assertThat(e.getMessage(), Matchers.containsString("ServletContext"));
49+
}
50+
}
51+
}

0 commit comments

Comments
 (0)