Fixed: Configuration.setServletContextForTemplateLoading did not support Jakarta
diff --git a/freemarker-core/src/main/java/freemarker/template/Configuration.java b/freemarker-core/src/main/java/freemarker/template/Configuration.java
index d08f815..1948804 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 @@
      * 
      * @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
-                            .getConstructor(constructorParamTypes)
-                                    .newInstance(constructorParams));
         } catch (Exception e) {
-            throw new BugException(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);
+        } catch (Exception 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 0000000..4e96cb6
--- /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"));
+        }
+    }
+}