This is an automated email from the ASF dual-hosted git repository.

jdaugherty pushed a commit to branch corrupt-jar-fixes
in repository https://gitbox.apache.org/repos/asf/grails-core.git

commit ef1bfc6fc5843a015902446ad4c23d87823f4bd8
Author: James Daugherty <[email protected]>
AuthorDate: Fri May 16 10:16:11 2025 -0400

    Revert "Various api cleanup for update to java 17"
    
    This reverts commit 8a4414bfdd6295623a3144064332c061456d9400.
---
 .../internal/DelegateAsyncTransformation.java      |  53 +++++---
 .../cache/GrailsConcurrentLinkedMapCache.java      |  30 +++--
 .../compiler/web/ControllerActionTransformer.java  |  59 ++++----
 .../main/groovy/grails/plugins/GrailsPlugin.java   |  47 ++++---
 .../groovy/grails/util/GrailsStringUtils.groovy    |  28 ++--
 .../org/grails/plugins/DefaultGrailsPlugin.java    | 134 ++++++++++--------
 .../org/grails/buffer/GrailsPrintWriter.java       |  45 ++++---
 .../org/grails/charsequences/CharSequences.java    |  28 ++--
 .../main/groovy/grails/util/BuildSettings.groovy   | 107 +++++++++++++++
 .../org/grails/gsp/GroovyPagesTemplateEngine.java  | 107 ++++++++-------
 .../org/grails/gsp/ModelRecordingGroovyPage.groovy |  36 ++---
 .../taglib/AbstractTemplateVariableBinding.java    |  39 +++---
 .../web/gsp/GroovyPagesTemplateRenderer.java       |  48 +++----
 .../org/grails/gsp/jsp/GroovyPagesPageContext.java |  65 ++++-----
 .../groovy/org/grails/web/json/JSONObject.java     |  46 +++----
 .../context/GrailsWebApplicationContext.java       |  44 +++---
 .../grails/web/databinding/DataBindingUtils.java   |  73 +++++-----
 .../grails/web/errors/GrailsExceptionResolver.java |  60 ++++-----
 .../org/grails/web/mapping/RegexUrlMapping.java    | 149 +++++++++++----------
 19 files changed, 678 insertions(+), 520 deletions(-)

diff --git 
a/grails-async/core/src/main/groovy/org/grails/async/transform/internal/DelegateAsyncTransformation.java
 
b/grails-async/core/src/main/groovy/org/grails/async/transform/internal/DelegateAsyncTransformation.java
index fcacba60d1..74fd98d37d 100644
--- 
a/grails-async/core/src/main/groovy/org/grails/async/transform/internal/DelegateAsyncTransformation.java
+++ 
b/grails-async/core/src/main/groovy/org/grails/async/transform/internal/DelegateAsyncTransformation.java
@@ -1,18 +1,20 @@
 /*
- *  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
+ *  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
  *
- *      https://www.apache.org/licenses/LICENSE-2.0
+ *    https://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.
+ *  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.grails.async.transform.internal;
 
@@ -20,6 +22,14 @@ import grails.async.Promise;
 import grails.async.Promises;
 import groovy.lang.Closure;
 import groovy.lang.GroovyObjectSupport;
+
+import java.beans.Introspector;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
 import org.apache.grails.common.compiler.GroovyTransformOrder;
 import org.codehaus.groovy.GroovyBugError;
 import org.codehaus.groovy.ast.ASTNode;
@@ -49,13 +59,6 @@ import org.codehaus.groovy.transform.GroovyASTTransformation;
 import org.codehaus.groovy.transform.TransformWithPriority;
 import org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport;
 
-import java.beans.Introspector;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
 /**
  * Implementation of {@link grails.async.DelegateAsync} transformation
  *
@@ -70,14 +73,18 @@ public class DelegateAsyncTransformation implements 
ASTTransformation, Transform
     public static final ClassNode OBJECT_CLASS_NODE = new 
ClassNode(Object.class);
 
     public void visit(ASTNode[] nodes, SourceUnit source) {
-        if (nodes.length != 2 || !(nodes[0] instanceof AnnotationNode 
annotationNode) || !(nodes[1] instanceof AnnotatedNode parent)) {
+        if (nodes.length != 2 || !(nodes[0] instanceof AnnotationNode) || 
!(nodes[1] instanceof AnnotatedNode)) {
             throw new GroovyBugError("Internal error: expecting 
[AnnotationNode, AnnotatedNode] but got: " + Arrays.asList(nodes));
         }
 
-        if (parent instanceof ClassNode classNode) {
+        AnnotatedNode parent = (AnnotatedNode) nodes[1];
+        AnnotationNode annotationNode = (AnnotationNode) nodes[0];
+
+        if (parent instanceof ClassNode) {
             Expression value = annotationNode.getMember("value");
             if (value instanceof ClassExpression) {
                 ClassNode targetApi = value.getType().getPlainNodeReference();
+                ClassNode classNode = (ClassNode)parent;
 
                 final String fieldName = '$' + 
Introspector.decapitalize(targetApi.getNameWithoutPackage());
                 FieldNode fieldNode = classNode.getField(fieldName);
@@ -88,7 +95,9 @@ public class DelegateAsyncTransformation implements 
ASTTransformation, Transform
 
                 applyDelegateAsyncTransform(classNode, targetApi, fieldName);
             }
-        } else if (parent instanceof FieldNode fieldNode) {
+        }
+        else if(parent instanceof FieldNode) {
+            FieldNode fieldNode = (FieldNode)parent;
             ClassNode targetApi = fieldNode.getType().getPlainNodeReference();
             ClassNode classNode = fieldNode.getOwner();
             applyDelegateAsyncTransform(classNode, targetApi, 
fieldNode.getName());
diff --git 
a/grails-cache/src/main/groovy/grails/plugin/cache/GrailsConcurrentLinkedMapCache.java
 
b/grails-cache/src/main/groovy/grails/plugin/cache/GrailsConcurrentLinkedMapCache.java
index ade7ac0efe..f7530e86f0 100644
--- 
a/grails-cache/src/main/groovy/grails/plugin/cache/GrailsConcurrentLinkedMapCache.java
+++ 
b/grails-cache/src/main/groovy/grails/plugin/cache/GrailsConcurrentLinkedMapCache.java
@@ -1,18 +1,20 @@
 /*
- *  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
+ *  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
  *
- *      https://www.apache.org/licenses/LICENSE-2.0
+ *    https://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.
+ *  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 grails.plugin.cache;
 
@@ -30,7 +32,7 @@ import java.util.concurrent.ConcurrentMap;
 public class GrailsConcurrentLinkedMapCache implements GrailsCache {
 
    private static final Object NULL_HOLDER = new NullHolder();
-    private final String name;
+   private String name;
    private long capacity;
    private final ConcurrentLinkedHashMap<Object, Object> store;
    private final boolean allowNullValues;
@@ -164,6 +166,8 @@ public class GrailsConcurrentLinkedMapCache implements 
GrailsCache {
       return (value != null ? new SimpleValueWrapper(fromStoreValue(value)) : 
null);
    }
 
+   @SuppressWarnings("serial")
+   
    private static class NullHolder implements Serializable {
    }
 }
diff --git 
a/grails-controllers/src/main/groovy/org/grails/compiler/web/ControllerActionTransformer.java
 
b/grails-controllers/src/main/groovy/org/grails/compiler/web/ControllerActionTransformer.java
index ec48903e1e..3de3f27943 100644
--- 
a/grails-controllers/src/main/groovy/org/grails/compiler/web/ControllerActionTransformer.java
+++ 
b/grails-controllers/src/main/groovy/org/grails/compiler/web/ControllerActionTransformer.java
@@ -1,18 +1,20 @@
 /*
- *  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
+ *  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
  *
- *      https://www.apache.org/licenses/LICENSE-2.0
+ *    https://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.
+ *  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.grails.compiler.web;
 
@@ -98,18 +100,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.regex.Pattern;
 
-import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.assignX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.boolX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.callThisX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.classX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.declX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.ifS;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.*;
 import static 
org.grails.compiler.injection.GrailsASTUtils.applyDefaultMethodTarget;
 import static org.grails.compiler.injection.GrailsASTUtils.applyMethodTarget;
 import static 
org.grails.compiler.injection.GrailsASTUtils.buildGetMapExpression;
@@ -195,7 +186,7 @@ public class ControllerActionTransformer implements 
GrailsArtefactClassInjector,
             ClassHelper.Boolean_TYPE, "boolean",
             ClassHelper.Byte_TYPE, "byte",
             ClassHelper.Character_TYPE, "char");
-    private static final List<ClassNode> PRIMITIVE_CLASS_NODES = 
CollectionUtils.newList(
+    private static List<ClassNode> PRIMITIVE_CLASS_NODES = 
CollectionUtils.<ClassNode>newList(
             ClassHelper.boolean_TYPE,
             ClassHelper.char_TYPE,
             ClassHelper.int_TYPE,
@@ -208,7 +199,7 @@ public class ControllerActionTransformer implements 
GrailsArtefactClassInjector,
 
     public static final String CONVERT_CLOSURES_KEY = 
"grails.compile.artefacts.closures.convert";
 
-    private final Boolean converterEnabled;
+    private Boolean converterEnabled;
     private CompilationUnit compilationUnit;
 
     public ControllerActionTransformer() {
@@ -243,7 +234,7 @@ public class ControllerActionTransformer implements 
GrailsArtefactClassInjector,
 
     private boolean isExceptionHandlingMethod(MethodNode methodNode) {
         boolean isExceptionHandler = false;
-        if (!methodNode.isPrivate() && !methodNode.getName().contains("$")) {
+        if(!methodNode.isPrivate() && methodNode.getName().indexOf("$") == -1) 
{
             Parameter[] parameters = methodNode.getParameters();
             if(parameters.length == 1) {
                 ClassNode parameterTypeClassNode = parameters[0].getType();
@@ -258,7 +249,7 @@ public class ControllerActionTransformer implements 
GrailsArtefactClassInjector,
     private void processMethods(ClassNode classNode, SourceUnit source,
             GeneratorContext context) {
 
-        List<MethodNode> deferredNewMethods = new ArrayList<>();
+        List<MethodNode> deferredNewMethods = new ArrayList<MethodNode>();
         for (MethodNode method : classNode.getMethods()) {
             if (methodShouldBeConfiguredAsControllerAction(method)) {
                 final List<MethodNode> declaredMethodsWithThisName = 
classNode.getDeclaredMethods(method.getName());
@@ -334,7 +325,7 @@ public class ControllerActionTransformer implements 
GrailsArtefactClassInjector,
     }
 
     protected Collection<MethodNode> getExceptionHandlerMethods(final 
ClassNode classNode, SourceUnit sourceUnit) {
-        final Map<ClassNode, MethodNode> exceptionTypeToHandlerMethodMap = new 
HashMap<>();
+        final Map<ClassNode, MethodNode> exceptionTypeToHandlerMethodMap = new 
HashMap<ClassNode, MethodNode>();
         final List<MethodNode> methods = classNode.getMethods();
         for(MethodNode methodNode : methods) {
             if(isExceptionHandlingMethod(methodNode)) {
@@ -456,7 +447,7 @@ public class ControllerActionTransformer implements 
GrailsArtefactClassInjector,
 
     private void processClosures(ClassNode classNode, SourceUnit source, 
GeneratorContext context) {
 
-        List<PropertyNode> propertyNodes = new 
ArrayList<>(classNode.getProperties());
+        List<PropertyNode> propertyNodes = new 
ArrayList<PropertyNode>(classNode.getProperties());
 
         Expression initialExpression;
         ClosureExpression closureAction;
@@ -531,12 +522,14 @@ public class ControllerActionTransformer implements 
GrailsArtefactClassInjector,
         
         if(allowedMethodsField != null) {
             final Expression initialAllowedMethodsExpression = 
allowedMethodsField.getInitialExpression();
-            if (initialAllowedMethodsExpression instanceof MapExpression 
allowedMethodsMapExpression) {
+            if(initialAllowedMethodsExpression instanceof MapExpression) {
                 boolean actionIsRestricted = false;
+                final MapExpression allowedMethodsMapExpression = 
(MapExpression) initialAllowedMethodsExpression;
                 final List<MapEntryExpression> 
allowedMethodsMapEntryExpressions = 
allowedMethodsMapExpression.getMapEntryExpressions();
                 for(MapEntryExpression allowedMethodsMapEntryExpression : 
allowedMethodsMapEntryExpressions) {
                     final Expression allowedMethodsMapEntryKeyExpression = 
allowedMethodsMapEntryExpression.getKeyExpression();
-                    if (allowedMethodsMapEntryKeyExpression instanceof 
ConstantExpression allowedMethodsMapKeyConstantExpression) {
+                    if(allowedMethodsMapEntryKeyExpression instanceof 
ConstantExpression) {
+                        final ConstantExpression 
allowedMethodsMapKeyConstantExpression = (ConstantExpression) 
allowedMethodsMapEntryKeyExpression;
                         final Object allowedMethodsMapKeyValue = 
allowedMethodsMapKeyConstantExpression.getValue();
                         if(methodName.equals(allowedMethodsMapKeyValue)) {
                             actionIsRestricted = true;
@@ -732,7 +725,7 @@ public class ControllerActionTransformer implements 
GrailsArtefactClassInjector,
                         Parameter[] parameters = 
methods.get(0).getParameters();
                         //Look for a parameter of index (argX) in the method.
                         //The $self is the first parameter, so arg1 == index 
of 1
-                        int argNum = 
Integer.parseInt(paramName.replaceFirst("arg", ""));
+                        int argNum = 
Integer.valueOf(paramName.replaceFirst("arg", ""));
                         if (parameters.length >= argNum + 1) {
                             Parameter helperParam = parameters[argNum];
                             //Set the request parameter name based off of the 
parameter in the trait helper method
diff --git a/grails-core/src/main/groovy/grails/plugins/GrailsPlugin.java 
b/grails-core/src/main/groovy/grails/plugins/GrailsPlugin.java
index 0dc3cb0746..4222206163 100644
--- a/grails-core/src/main/groovy/grails/plugins/GrailsPlugin.java
+++ b/grails-core/src/main/groovy/grails/plugins/GrailsPlugin.java
@@ -1,36 +1,39 @@
 /*
- *  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
+ *  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
  *
- *      https://www.apache.org/licenses/LICENSE-2.0
+ *    https://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.
+ *  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 grails.plugins;
 
-import grails.core.GrailsApplication;
 import grails.util.Environment;
 import groovy.lang.GroovyObject;
-import org.grails.plugins.support.WatchPattern;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import grails.core.GrailsApplication;
 import org.grails.spring.RuntimeSpringConfiguration;
+import org.grails.plugins.support.WatchPattern;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContextAware;
 import org.springframework.core.env.PropertySource;
 import org.springframework.core.io.Resource;
 import org.springframework.core.type.filter.TypeFilter;
 
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
 /**
  * <p>Plugin interface that adds Spring {@link 
org.springframework.beans.factory.config.BeanDefinition}s
  * to a registry based on a {@link GrailsApplication} object. After all 
<code>GrailsPlugin</code> classes
@@ -211,6 +214,14 @@ public interface GrailsPlugin extends 
ApplicationContextAware, Comparable, Grail
      */
     boolean supportsCurrentScopeAndEnvironment();
 
+    /**
+     * Write some documentation to the DocumentationContext
+     * @deprecated Dynamic document generation no longer supported
+     * @param text
+     */
+    @Deprecated
+    void doc(String text);
+
     /**
      * Returns the path of the plug-in
      *
diff --git a/grails-core/src/main/groovy/grails/util/GrailsStringUtils.groovy 
b/grails-core/src/main/groovy/grails/util/GrailsStringUtils.groovy
index 1ed039edd4..3e1b73351e 100644
--- a/grails-core/src/main/groovy/grails/util/GrailsStringUtils.groovy
+++ b/grails-core/src/main/groovy/grails/util/GrailsStringUtils.groovy
@@ -1,18 +1,20 @@
 /*
- *  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
+ *  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
  *
- *      https://www.apache.org/licenses/LICENSE-2.0
+ *    https://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.
+ *  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 grails.util
 
@@ -28,7 +30,7 @@ import java.util.regex.Pattern
  * @since 2.3.6
  */
 @CompileStatic
-abstract class GrailsStringUtils extends StringUtils {
+abstract class GrailsStringUtils extends StringUtils{
 
     private static final Pattern BOOLEAN_PATTERN = 
Pattern.compile(/^on$|^true$|^yes$|^1$/, Pattern.CASE_INSENSITIVE)
 
diff --git 
a/grails-core/src/main/groovy/org/grails/plugins/DefaultGrailsPlugin.java 
b/grails-core/src/main/groovy/org/grails/plugins/DefaultGrailsPlugin.java
index c7b5823ac0..8e9a1f93fd 100644
--- a/grails-core/src/main/groovy/org/grails/plugins/DefaultGrailsPlugin.java
+++ b/grails-core/src/main/groovy/org/grails/plugins/DefaultGrailsPlugin.java
@@ -1,48 +1,59 @@
 /*
- *  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
+ *  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
  *
- *      https://www.apache.org/licenses/LICENSE-2.0
+ *    https://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.
+ *  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.grails.plugins;
 
-import grails.core.ArtefactHandler;
-import grails.core.GrailsApplication;
-import grails.core.support.GrailsApplicationAware;
-import grails.core.support.ParentApplicationContextAware;
 import grails.plugins.GrailsPlugin;
 import grails.plugins.GrailsPluginManager;
 import grails.plugins.Plugin;
-import grails.plugins.exceptions.PluginException;
 import grails.spring.BeanBuilder;
 import grails.util.CollectionUtils;
 import grails.util.Environment;
-import grails.util.GrailsArrayUtils;
-import grails.util.GrailsClassUtils;
 import grails.util.GrailsUtil;
-import groovy.lang.Binding;
-import groovy.lang.Closure;
-import groovy.lang.GroovyClassLoader;
-import groovy.lang.GroovyObject;
-import groovy.lang.MetaClass;
+import groovy.lang.*;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+import grails.core.ArtefactHandler;
+import grails.core.GrailsApplication;
+import grails.util.GrailsArrayUtils;
+import grails.util.GrailsClassUtils;
 import org.grails.core.io.CachingPathMatchingResourcePatternResolver;
 import org.grails.core.io.SpringResource;
+import org.grails.spring.RuntimeSpringConfiguration;
+import grails.plugins.exceptions.PluginException;
 import org.grails.plugins.support.WatchPattern;
 import org.grails.plugins.support.WatchPatternParser;
-import org.grails.spring.RuntimeSpringConfiguration;
+import grails.core.support.GrailsApplicationAware;
+import grails.core.support.ParentApplicationContextAware;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
 import org.springframework.beans.BeanWrapper;
 import org.springframework.beans.BeanWrapperImpl;
 import org.springframework.beans.BeansException;
@@ -57,19 +68,6 @@ import org.springframework.core.io.Resource;
 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
 import org.springframework.core.type.filter.TypeFilter;
 
-import java.io.File;
-import java.io.IOException;
-import java.io.Serial;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
 /**
  * Implementation of the GrailsPlugin interface that wraps a Groovy plugin 
class
  * and provides the magic to invoke its various methods from Java.
@@ -107,8 +105,8 @@ public class DefaultGrailsPlugin extends 
AbstractGrailsPlugin implements ParentA
     private Class<?>[] providedArtefacts = {};
     private Collection profiles = null;
     private Map pluginEnvs;
-    private List<String> pluginExcludes = new ArrayList<>();
-    private Collection<? extends TypeFilter> typeFilters = new ArrayList<>();
+    private List<String> pluginExcludes = new ArrayList<String>();
+    private Collection<? extends TypeFilter> typeFilters = new 
ArrayList<TypeFilter>();
     private Resource pluginDescriptor;
     private List<WatchPattern> watchedResourcePatterns;
 
@@ -176,7 +174,8 @@ public class DefaultGrailsPlugin extends 
AbstractGrailsPlugin implements ParentA
     private void initialisePlugin(Class<?> clazz) {
         pluginGrailsClass = new GrailsPluginClass(clazz);
         plugin = (GroovyObject)pluginGrailsClass.newInstance();
-        if (plugin instanceof Plugin p) {
+        if(plugin instanceof Plugin) {
+            Plugin p = (Plugin)plugin;
             p.setApplicationContext(applicationContext);
             p.setPlugin(this);
             p.setGrailsApplication(grailsApplication);
@@ -220,7 +219,6 @@ public class DefaultGrailsPlugin extends 
AbstractGrailsPlugin implements ParentA
     private void evaluatePluginScopes() {
         // Damn I wish Java had closures
         pluginEnvs = evaluateIncludeExcludeProperty(ENVIRONMENTS, new 
Closure(this) {
-            @Serial
             private static final long serialVersionUID = 1;
             @Override
             public Object call(Object arguments) {
@@ -235,7 +233,8 @@ public class DefaultGrailsPlugin extends 
AbstractGrailsPlugin implements ParentA
     private Map evaluateIncludeExcludeProperty(String name, Closure converter) 
{
         Map resultMap = new HashMap();
         Object propertyValue = 
GrailsClassUtils.getPropertyOrStaticPropertyOrFieldValue(plugin, name);
-        if (propertyValue instanceof Map containedMap) {
+        if (propertyValue instanceof Map) {
+            Map containedMap = (Map)propertyValue;
 
             Object includes = containedMap.get(INCLUDES);
             evaluateAndAddIncludeExcludeObject(resultMap, includes, true, 
converter);
@@ -250,16 +249,20 @@ public class DefaultGrailsPlugin extends 
AbstractGrailsPlugin implements ParentA
     }
 
     private void evaluateAndAddIncludeExcludeObject(Map targetMap, Object 
includeExcludeObject, boolean include, Closure converter) {
-        if (includeExcludeObject instanceof String includeExcludeString) {
+        if (includeExcludeObject instanceof String) {
+            final String includeExcludeString = (String) includeExcludeObject;
             evaluateAndAddToIncludeExcludeSet(targetMap,includeExcludeString, 
include, converter);
-        } else if (includeExcludeObject instanceof List includeExcludeList) {
+        }
+        else if (includeExcludeObject instanceof List) {
+            List includeExcludeList = (List) includeExcludeObject;
             evaluateAndAddListOfValues(targetMap,includeExcludeList, include, 
converter);
         }
     }
 
     private void evaluateAndAddListOfValues(Map targetMap, List 
includeExcludeList, boolean include, Closure converter) {
         for (Object value : includeExcludeList) {
-            if (value instanceof String scopeName) {
+            if (value instanceof String) {
+                final String scopeName = (String) value;
                 evaluateAndAddToIncludeExcludeSet(targetMap, scopeName, 
include, converter);
             }
         }
@@ -285,8 +288,9 @@ public class DefaultGrailsPlugin extends 
AbstractGrailsPlugin implements ParentA
     @SuppressWarnings("unchecked")
     private void evaluateProvidedArtefacts() {
         Object result = 
GrailsClassUtils.getPropertyOrStaticPropertyOrFieldValue(pluginBean, plugin, 
PROVIDED_ARTEFACTS);
-        if (result instanceof Collection artefactList) {
-            providedArtefacts = (Class<?>[]) artefactList.toArray(new 
Class[0]);
+        if (result instanceof Collection) {
+            final Collection artefactList = (Collection) result;
+            providedArtefacts = (Class<?>[])artefactList.toArray(new 
Class[artefactList.size()]);
         }
     }
 
@@ -305,7 +309,8 @@ public class DefaultGrailsPlugin extends 
AbstractGrailsPlugin implements ParentA
     private void evaluateObservedPlugins() {
         if (pluginBean.isReadableProperty(OBSERVE)) {
             Object observeProperty = 
GrailsClassUtils.getPropertyOrStaticPropertyOrFieldValue(pluginBean, plugin, 
OBSERVE);
-            if (observeProperty instanceof Collection observeList) {
+            if (observeProperty instanceof Collection) {
+                Collection observeList = (Collection)observeProperty;
                 observedPlugins = new String[observeList.size()];
                 int j = 0;
                 for (Object anObserveList : observeList) {
@@ -342,7 +347,7 @@ public class DefaultGrailsPlugin extends 
AbstractGrailsPlugin implements ParentA
         }
 
         Environment env = Environment.getCurrent();
-        final boolean warDeployed = Environment.isWarDeployed();
+        final boolean warDeployed = env.isWarDeployed();
         final boolean reloadEnabled = env.isReloadEnabled();
 
         if (!((reloadEnabled || !warDeployed))) {
@@ -367,7 +372,7 @@ public class DefaultGrailsPlugin extends 
AbstractGrailsPlugin implements ParentA
                 return;
             }
 
-            List<String> resourceListTmp = new ArrayList<>();
+            List<String> resourceListTmp = new ArrayList<String>();
             final String baseLocation = env.getReloadLocation();
 
             for (Object ref : resourceList) {
@@ -448,13 +453,13 @@ public class DefaultGrailsPlugin extends 
AbstractGrailsPlugin implements ParentA
         if (pluginBean.isReadableProperty(PLUGIN_LOAD_AFTER_NAMES)) {
             List loadAfterNamesList = (List) 
GrailsClassUtils.getPropertyOrStaticPropertyOrFieldValue(pluginBean, plugin, 
PLUGIN_LOAD_AFTER_NAMES);
             if (loadAfterNamesList != null) {
-                loadAfterNames = (String[]) loadAfterNamesList.toArray(new 
String[0]);
+                loadAfterNames = (String[])loadAfterNamesList.toArray(new 
String[loadAfterNamesList.size()]);
             }
         }
         if (pluginBean.isReadableProperty(PLUGIN_LOAD_BEFORE_NAMES)) {
             List loadBeforeNamesList = (List) 
GrailsClassUtils.getPropertyOrStaticPropertyOrFieldValue(pluginBean, plugin, 
PLUGIN_LOAD_BEFORE_NAMES);
             if (loadBeforeNamesList != null) {
-                loadBeforeNames = (String[]) loadBeforeNamesList.toArray(new 
String[0]);
+                loadBeforeNames = (String[])loadBeforeNamesList.toArray(new 
String[loadBeforeNamesList.size()]);
             }
         }
     }
@@ -466,7 +471,7 @@ public class DefaultGrailsPlugin extends 
AbstractGrailsPlugin implements ParentA
         }
 
         dependencies = (Map) 
GrailsClassUtils.getPropertyOrStaticPropertyOrFieldValue(pluginBean, plugin, 
DEPENDS_ON);
-        dependencyNames = dependencies.keySet().toArray(new String[0]);
+        dependencyNames = dependencies.keySet().toArray(new 
String[dependencies.size()]);
     }
 
     @Override
@@ -497,7 +502,9 @@ public class DefaultGrailsPlugin extends 
AbstractGrailsPlugin implements ParentA
     }
 
     public void doWithApplicationContext(ApplicationContext ctx) {
-        if (plugin instanceof Plugin pluginObject) {
+        if(plugin instanceof Plugin) {
+            Plugin pluginObject = (Plugin) plugin;
+
             pluginObject.setApplicationContext(ctx);
             pluginObject.doWithApplicationContext();
         }
@@ -600,6 +607,15 @@ public class DefaultGrailsPlugin extends 
AbstractGrailsPlugin implements ParentA
         return !(excludes != null && excludes.contains(value));
     }
 
+    /**
+     * @deprecated Dynamic document generation no longer supported
+     * @param text
+     */
+    @Deprecated
+    public void doc(String text) {
+        // no-op
+    }
+
     @Override
     public String[] getDependencyNames() {
         return dependencyNames;
@@ -779,12 +795,13 @@ public class DefaultGrailsPlugin extends 
AbstractGrailsPlugin implements ParentA
         onChangeListener.setDelegate(this);
         onChangeListener.call(new Object[]{event});
 
-        if (!(applicationContext instanceof GenericApplicationContext ctx)) {
+        if (!(applicationContext instanceof GenericApplicationContext)) {
             return;
         }
 
         // Apply any factory post processors in case the change listener has 
changed any
         // bean definitions (GRAILS-5763)
+        GenericApplicationContext ctx = (GenericApplicationContext) 
applicationContext;
         ConfigurableListableBeanFactory beanFactory = ctx.getBeanFactory();
         for (BeanFactoryPostProcessor postProcessor : 
ctx.getBeanFactoryPostProcessors()) {
             try {
@@ -810,7 +827,8 @@ public class DefaultGrailsPlugin extends 
AbstractGrailsPlugin implements ParentA
             l = (List)plugin.getProperty(ARTEFACTS);
         }
         for (Object artefact : l) {
-            if (artefact instanceof Class artefactClass) {
+            if (artefact instanceof Class) {
+                Class artefactClass = (Class) artefact;
                 if (ArtefactHandler.class.isAssignableFrom(artefactClass)) {
                     try {
                         
grailsApplication.registerArtefactHandler((ArtefactHandler) 
artefactClass.newInstance());
diff --git 
a/grails-encoder/src/main/groovy/org/grails/buffer/GrailsPrintWriter.java 
b/grails-encoder/src/main/groovy/org/grails/buffer/GrailsPrintWriter.java
index 873948e900..df4fb5f60e 100644
--- a/grails-encoder/src/main/groovy/org/grails/buffer/GrailsPrintWriter.java
+++ b/grails-encoder/src/main/groovy/org/grails/buffer/GrailsPrintWriter.java
@@ -1,29 +1,33 @@
 /*
- *  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
+ *  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
  *
- *      https://www.apache.org/licenses/LICENSE-2.0
+ *    https://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.
+ *  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.grails.buffer;
 
 import groovy.lang.GroovyObject;
 import groovy.lang.MetaClass;
 import groovy.lang.Writable;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
+
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.codehaus.groovy.runtime.GStringImpl;
-import org.codehaus.groovy.runtime.InvokerHelper;
-import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
 import org.grails.charsequences.CharSequences;
 import org.grails.encoder.EncodedAppender;
 import org.grails.encoder.EncodedAppenderFactory;
@@ -33,10 +37,9 @@ import org.grails.encoder.Encoder;
 import org.grails.encoder.EncodingStateRegistry;
 import org.grails.encoder.StreamingEncoder;
 import org.grails.encoder.StreamingEncoderWriter;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.Writer;
+import org.codehaus.groovy.runtime.GStringImpl;
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
 
 /**
  * PrintWriter implementation that doesn't have synchronization. null object
@@ -46,7 +49,7 @@ import java.io.Writer;
  */
 public class GrailsPrintWriter extends Writer implements GrailsWrappedWriter, 
EncodedAppenderWriterFactory, GroovyObject {
     protected static final Log LOG = 
LogFactory.getLog(GrailsPrintWriter.class);
-    protected static final char[] CRLF = {'\r', '\n'};
+    protected static final char CRLF[] = { '\r', '\n' };
     protected boolean trouble = false;
     protected Writer out;
     protected boolean allowUnwrappingOut = true;
@@ -98,7 +101,7 @@ public class GrailsPrintWriter extends Writer implements 
GrailsWrappedWriter, En
      *
      * @param obj The value
      * @return Returns this object
-     * @throws IOException If an I/O error occurs
+     * @throws IOException
      */
     public GrailsPrintWriter leftShift(Object obj) throws IOException {
         if (trouble || obj == null) {
diff --git 
a/grails-encoder/src/main/groovy/org/grails/charsequences/CharSequences.java 
b/grails-encoder/src/main/groovy/org/grails/charsequences/CharSequences.java
index 05c5677949..a814e50b7c 100644
--- a/grails-encoder/src/main/groovy/org/grails/charsequences/CharSequences.java
+++ b/grails-encoder/src/main/groovy/org/grails/charsequences/CharSequences.java
@@ -1,18 +1,20 @@
 /*
- *  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
+ *  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
  *
- *      https://www.apache.org/licenses/LICENSE-2.0
+ *    https://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.
+ *  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.grails.charsequences;
 
@@ -82,7 +84,7 @@ public class CharSequences {
      * @param csq source CharSequence instance
      * @param start start/offset index
      * @param end end index + 1
-     * @throws IOException If an I/O error occurs
+     * @throws IOException
      */
     public static void writeCharSequence(Writer target, CharSequence csq, int 
start, int end) throws IOException {
         final Class<?> csqClass = csq.getClass();
diff --git 
a/grails-gradle/model/src/main/groovy/grails/util/BuildSettings.groovy 
b/grails-gradle/model/src/main/groovy/grails/util/BuildSettings.groovy
index 915d696a93..dd7dd52d89 100644
--- a/grails-gradle/model/src/main/groovy/grails/util/BuildSettings.groovy
+++ b/grails-gradle/model/src/main/groovy/grails/util/BuildSettings.groovy
@@ -19,6 +19,8 @@ package grails.util
 import grails.io.IOUtils
 import groovy.transform.CompileStatic
 
+import java.util.regex.Pattern
+
 /**
  * Build time settings and configuration
  *
@@ -57,21 +59,118 @@ class BuildSettings {
      */
     public static final String PROFILE_REPOSITORIES = 
"grails.profiles.repositories"
 
+    public static final String BUILD_SCOPE = "build"
+    public static final String COMPILE_SCOPE = "compileClasspath"
+    public static final String RUNTIME_SCOPE = "runtimeClasspath"
+    public static final String TEST_SCOPE = "testCompileClasspath"
+    public static final String PROVIDED_SCOPE = "provided"
+
+    public static final String BUILD_SCOPE_DESC = "Dependencies for the build 
system only"
+    public static final String COMPILE_SCOPE_DESC = "Dependencies placed on 
the classpath for compilation"
+    public static final String RUNTIME_SCOPE_DESC = "Dependencies needed at 
runtime but not for compilation"
+    public static final String TEST_SCOPE_DESC = "Dependencies needed for test 
compilation and execution but not at runtime"
+    public static final String PROVIDED_SCOPE_DESC = "Dependencies needed at 
development time, but not during deployment"
+
+    public static final Map<String, String> SCOPE_TO_DESC = [
+            (BUILD_SCOPE): BUILD_SCOPE_DESC,
+            (PROVIDED_SCOPE): PROVIDED_SCOPE_DESC,
+            (COMPILE_SCOPE): COMPILE_SCOPE_DESC,
+            (RUNTIME_SCOPE): RUNTIME_SCOPE_DESC,
+            (TEST_SCOPE): TEST_SCOPE_DESC
+    ]
+
+    public static final Pattern JAR_PATTERN = ~/^\S+\.jar$/
+
+    /**
+     * The compiler source level to use
+     */
+    public static final String COMPILER_SOURCE_LEVEL = 
"grails.project.source.level"
+
+    /**
+     * The compiler source level to use
+     */
+    public static final String COMPILER_TARGET_LEVEL = 
"grails.project.target.level"
+    /**
+     * The version of the servlet API
+     */
+    public static final String SERVLET_VERSION = "grails.servlet.version"
     /**
      * The base directory of the application
      */
     public static final String APP_BASE_DIR = "base.dir"
+    /**
+     * The name of the system property for the Grails work directory.
+     */
+    public static final String WORK_DIR = "grails.work.dir"
+
+    /**
+     * The name of the system property for the project work directory
+     */
+    public static final String PROJECT_WORK_DIR = "grails.project.work.dir"
+
+    public static final String OFFLINE_MODE= "grails.offline.mode"
 
     /**
      * The name of the system property for {@link #}.
      */
     public static final String PROJECT_RESOURCES_DIR = 
"grails.project.resource.dir"
 
+    /**
+     * The name of the system property for project source directory. Must be 
set if changed from src/main/groovy
+     */
+    public static final String PROJECT_SOURCE_DIR = "grails.project.source.dir"
+
     /**
      * The name of the system property for the project classes directory. Must 
be set if changed from build/main/classes.
      */
     public static final String PROJECT_CLASSES_DIR = "grails.project.class.dir"
 
+    /**
+     * The name of the system property for project test classes directory. 
Must be set if changed from build/test/classes
+     */
+    public static final String PROJECT_TEST_CLASSES_DIR = 
"grails.project.test.class.dir"
+
+    /**
+     * The name of the system property for test reported directory
+     */
+    public static final String PROJECT_TEST_REPORTS_DIR = 
"grails.project.test.reports.dir"
+
+    /**
+     * The name of the system property for documentation output directory
+     */
+    public static final String PROJECT_DOCS_OUTPUT_DIR = 
"grails.project.docs.output.dir"
+
+    /**
+     * The name of the property specification test locations, must be set of 
the directory is changed from src/test/groovy
+     */
+    public static final String PROJECT_TEST_SOURCE_DIR = 
"grails.project.test.source.dir"
+
+    /**
+     * The name of the system property for the the project target directory. 
Must be set if Gradle build location is changed.
+     */
+    public static final String PROJECT_TARGET_DIR = "grails.project.target.dir"
+
+    /**
+     * The name of the WAR file of the project
+     */
+    public static final String PROJECT_WAR_FILE = "grails.project.war.file"
+
+    /**
+     * The name of the WAR file of the project
+     */
+    public static final String PROJECT_AUTODEPLOY_DIR = 
"grails.project.autodeploy.dir"
+
+    /**
+     * A system property with this name is populated in the preparation phase 
of functional testing
+     * with the base URL that tests should be run against.
+     */
+    public static final String FUNCTIONAL_BASE_URL_PROPERTY = 
'grails.testing.functional.baseUrl'
+
+    /**
+     * The name of the working directory for commands that don't belong to a 
project (like create-app)
+     */
+    public static final String CORE_WORKING_DIR_NAME = '.core'
+
     /**
      *  A property name to enable/disable AST conversion of closures 
actions&tags to methods
      */
@@ -99,6 +198,7 @@ class BuildSettings {
      * The classes directory of the project, null outside of the development 
environment
      */
     public static final File CLASSES_DIR
+    public static final String RUN_EXECUTED = "grails.run.executed"
 
     /**
      * The path to the build classes directory
@@ -119,6 +219,13 @@ class BuildSettings {
         BuildSettings.package.implementationVersion
     }
 
+    /**
+     * @return Whether the current version of Grails being used is a 
development version
+     */
+    static boolean isDevelopmentGrailsVersion() {
+        BuildSettings.package.implementationVersion.endsWith('-SNAPSHOT')
+    }
+
     static {
         boolean grailsAppDirPresent = new File( "grails-app").exists() || new 
File( "Application.groovy").exists()
         if(!grailsAppDirPresent) {
diff --git 
a/grails-gsp/core/src/main/groovy/org/grails/gsp/GroovyPagesTemplateEngine.java 
b/grails-gsp/core/src/main/groovy/org/grails/gsp/GroovyPagesTemplateEngine.java
index 205d26aeb8..fc107c6acf 100644
--- 
a/grails-gsp/core/src/main/groovy/org/grails/gsp/GroovyPagesTemplateEngine.java
+++ 
b/grails-gsp/core/src/main/groovy/org/grails/gsp/GroovyPagesTemplateEngine.java
@@ -1,18 +1,20 @@
 /*
- *  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
+ *  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
  *
- *      https://www.apache.org/licenses/LICENSE-2.0
+ *    https://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.
+ *  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.grails.gsp;
 
@@ -35,11 +37,7 @@ import org.grails.core.artefact.DomainClassArtefactHandler;
 import org.grails.core.exceptions.DefaultErrorsPrinter;
 import org.grails.exceptions.ExceptionUtils;
 import org.grails.gsp.compiler.GroovyPageParser;
-import org.grails.gsp.io.DefaultGroovyPageLocator;
-import org.grails.gsp.io.GroovyPageCompiledScriptSource;
-import org.grails.gsp.io.GroovyPageLocator;
-import org.grails.gsp.io.GroovyPageResourceScriptSource;
-import org.grails.gsp.io.GroovyPageScriptSource;
+import org.grails.gsp.io.*;
 import org.grails.gsp.jsp.TagLibraryResolver;
 import org.grails.taglib.TagLibraryLookup;
 import org.springframework.beans.BeansException;
@@ -48,11 +46,7 @@ import org.springframework.beans.factory.InitializingBean;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContextAware;
 import org.springframework.context.ResourceLoaderAware;
-import org.springframework.core.io.ByteArrayResource;
-import org.springframework.core.io.FileSystemResource;
-import org.springframework.core.io.Resource;
-import org.springframework.core.io.ResourceLoader;
-import org.springframework.core.io.UrlResource;
+import org.springframework.core.io.*;
 import org.springframework.scripting.ScriptSource;
 import org.springframework.scripting.support.ResourceScriptSource;
 import org.springframework.util.Assert;
@@ -61,12 +55,8 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
+import java.security.PrivilegedAction;
+import java.util.*;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
@@ -75,7 +65,7 @@ import java.util.concurrent.atomic.AtomicInteger;
 /**
  * Based on (but not extending) the existing TemplateEngine implementations
  * within Groovy. It allows GSP pages to be re-used in different context using 
code like the below:
- * <p>
+ *
  * <code>
  *      Template t = new GroovyPagesTemplateEngine()
  *                          .createTemplate(context,request,response);
@@ -98,9 +88,9 @@ public class GroovyPagesTemplateEngine extends 
ResourceAwareTemplateEngine imple
     private static final Log LOG = 
LogFactory.getLog(GroovyPagesTemplateEngine.class);
     private static File dumpLineNumbersTo;
 
-    private final ConcurrentMap<String, CacheEntry<GroovyPageMetaInfo>> 
pageCache = new ConcurrentHashMap<>();
+    private ConcurrentMap<String, CacheEntry<GroovyPageMetaInfo>> pageCache = 
new ConcurrentHashMap<String, CacheEntry<GroovyPageMetaInfo>>();
     private ClassLoader classLoader;
-    private final AtomicInteger scriptNameCount = new AtomicInteger(0);
+    private AtomicInteger scriptNameCount=new AtomicInteger(0);
 
     private GroovyPageLocator groovyPageLocator = new 
DefaultGroovyPageLocator();
 
@@ -113,7 +103,7 @@ public class GroovyPagesTemplateEngine extends 
ResourceAwareTemplateEngine imple
     private GrailsApplication grailsApplication;
     private Map<String, Class<?>> cachedDomainsWithoutPackage;
 
-    private List<GroovyPageSourceDecorator> groovyPageSourceDecorators = new 
ArrayList<>();
+    private List<GroovyPageSourceDecorator> groovyPageSourceDecorators = new 
ArrayList();
 
     static {
         String dirPath = 
System.getProperty("grails.dump.gsp.line.numbers.to.dir");
@@ -316,7 +306,7 @@ public class GroovyPagesTemplateEngine extends 
ResourceAwareTemplateEngine imple
      *
      * @param uri The URI of the page to create the template for
      * @return The Template instance
-     * @throws CompilationFailedException if the gsp compilation fails
+     * @throws CompilationFailedException
      */
     @Override
     public Template createTemplate(String uri) {
@@ -336,12 +326,14 @@ public class GroovyPagesTemplateEngine extends 
ResourceAwareTemplateEngine imple
     }
 
     private GroovyPageMetaInfo initializeCompiledMetaInfo(GroovyPageMetaInfo 
meta) {
-        meta.initializeOnDemand(metaInfo -> {
-            metaInfo.setGrailsApplication(grailsApplication);
-            metaInfo.setJspTagLibraryResolver(jspTagLibraryResolver);
-            metaInfo.setTagLibraryLookup(tagLibraryLookup);
-            metaInfo.initialize();
-            
GroovyPagesMetaUtils.registerMethodMissingForGSP(metaInfo.getPageClass(), 
tagLibraryLookup);
+        meta.initializeOnDemand(new 
GroovyPageMetaInfo.GroovyPageMetaInfoInitializer() {
+            public void initialize(GroovyPageMetaInfo metaInfo) {
+                metaInfo.setGrailsApplication(grailsApplication);
+                metaInfo.setJspTagLibraryResolver(jspTagLibraryResolver);
+                metaInfo.setTagLibraryLookup(tagLibraryLookup);
+                metaInfo.initialize();
+                
GroovyPagesMetaUtils.registerMethodMissingForGSP(metaInfo.getPageClass(), 
tagLibraryLookup);
+            }
         });
         return meta;
     }
@@ -379,7 +371,8 @@ public class GroovyPagesTemplateEngine extends 
ResourceAwareTemplateEngine imple
             return 
createTemplateFromPrecompiled((GroovyPageCompiledScriptSource) scriptSource);
         }
 
-        if (scriptSource instanceof ResourceScriptSource resourceSource) {
+        if (scriptSource instanceof ResourceScriptSource) {
+            ResourceScriptSource resourceSource = (ResourceScriptSource) 
scriptSource;
             Resource resource = resourceSource.getResource();
             return createTemplate(resource, true);
         }
@@ -399,14 +392,14 @@ public class GroovyPagesTemplateEngine extends 
ResourceAwareTemplateEngine imple
      * @param pageName The name of the page being parsed
      *
      * @return The Template instance
-     * @throws CompilationFailedException if gsp template compilation fails
+     * @throws CompilationFailedException
      * @throws IOException Thrown if an IO exception occurs creating the 
Template
      */
     public Template createTemplate(String txt, String pageName) throws 
IOException {
         Assert.hasLength(txt, "Argument [txt] cannot be null or blank");
         Assert.hasLength(pageName, "Argument [pageName] cannot be null or 
blank");
 
-        return createTemplate(new 
ByteArrayResource(txt.getBytes(StandardCharsets.UTF_8), pageName), pageName, 
pageName != null);
+        return createTemplate(new ByteArrayResource(txt.getBytes("UTF-8"), 
pageName), pageName, pageName != null);
     }
 
     /**
@@ -451,9 +444,13 @@ public class GroovyPagesTemplateEngine extends 
ResourceAwareTemplateEngine imple
     }
 
     protected GroovyPageMetaInfo buildPageMetaInfo(Resource resource, String 
pageName) throws IOException {
-        try (InputStream inputStream = resource.getInputStream()) {
+        InputStream inputStream = resource.getInputStream();
+        try {
             return buildPageMetaInfo(inputStream, resource, pageName);
         }
+        finally {
+            inputStream.close();
+        }
     }
 
     private StringBuilder decorateGroovyPageSource(StringBuilder source) 
throws IOException {
@@ -471,7 +468,11 @@ public class GroovyPagesTemplateEngine extends 
ResourceAwareTemplateEngine imple
      * @return true if it is reloadable
      */
     private boolean isGroovyPageReloadable(final Resource resource, 
GroovyPageMetaInfo meta) {
-        return isReloadEnabled() && meta.shouldReload(() -> resource);
+        return isReloadEnabled() && meta.shouldReload(new 
PrivilegedAction<Resource>() {
+            public Resource run() {
+                return resource;
+            }
+        });
     }
 
     /**
@@ -500,7 +501,7 @@ public class GroovyPagesTemplateEngine extends 
ResourceAwareTemplateEngine imple
      */
     public Resource getResourceForUri(String uri) {
         GroovyPageScriptSource scriptSource = getResourceWithinContext(uri);
-        if ((scriptSource instanceof GroovyPageResourceScriptSource)) {
+        if (scriptSource != null && (scriptSource instanceof 
GroovyPageResourceScriptSource)) {
             return 
((GroovyPageResourceScriptSource)scriptSource).getResource();
         }
         return null;
@@ -508,7 +509,11 @@ public class GroovyPagesTemplateEngine extends 
ResourceAwareTemplateEngine imple
 
     private GroovyPageScriptSource getResourceWithinContext(String uri) {
         Assert.state(groovyPageLocator != null, "TemplateEngine not 
initialised correctly, no [groovyPageLocator] specified!");
-        return groovyPageLocator.findPage(uri);
+        GroovyPageScriptSource scriptSource = groovyPageLocator.findPage(uri);
+        if (scriptSource != null) {
+            return scriptSource;
+        }
+        return null;
     }
 
     /**
@@ -682,7 +687,11 @@ public class GroovyPagesTemplateEngine extends 
ResourceAwareTemplateEngine imple
             // a word character or a digit with an underscore
             if (name.startsWith("/")) name = name.substring(1);
             return name.replaceAll("[^\\w\\d]", "_");
-        } catch (IllegalStateException | IOException e) {
+        }
+        catch (IllegalStateException e) {
+            return generateTemplateName();
+        }
+        catch (IOException ioex) {
             return generateTemplateName();
         }
     }
@@ -772,12 +781,12 @@ public class GroovyPagesTemplateEngine extends 
ResourceAwareTemplateEngine imple
     /**
      * The domainClassMap is used in GSP binding to "auto-import" domain 
classes in packages without package prefix.
      * real imports aren't used, instead each class is added to the binding
-     * <p>
+     *
      * This feature has existed earlier, the code has just been refactored and 
moved to GroovyPagesTemplateEngine
      * to prevent using the static cache that was used previously.
      */
     private Map<String, Class<?>> createDomainClassMap() {
-        Map<String, Class<?>> domainsWithoutPackage = new HashMap<>();
+        Map<String, Class<?>> domainsWithoutPackage = new HashMap<String, 
Class<?>>();
         if (grailsApplication != null) {
             GrailsClass[] domainClasses = 
grailsApplication.getArtefacts(DomainClassArtefactHandler.TYPE);
             for (GrailsClass domainClass : domainClasses) {
diff --git 
a/grails-gsp/core/src/main/groovy/org/grails/gsp/ModelRecordingGroovyPage.groovy
 
b/grails-gsp/core/src/main/groovy/org/grails/gsp/ModelRecordingGroovyPage.groovy
index 574050f24b..2194a9c4c2 100644
--- 
a/grails-gsp/core/src/main/groovy/org/grails/gsp/ModelRecordingGroovyPage.groovy
+++ 
b/grails-gsp/core/src/main/groovy/org/grails/gsp/ModelRecordingGroovyPage.groovy
@@ -1,18 +1,20 @@
 /*
- *  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
+ *  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
  *
- *      https://www.apache.org/licenses/LICENSE-2.0
+ *    https://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.
+ *  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.grails.gsp
 
@@ -39,8 +41,12 @@ import java.util.concurrent.ConcurrentHashMap
  */
 @CompileStatic
 abstract class ModelRecordingGroovyPage extends GroovyPage {
-    public static final String CONFIG_SYSTEM_PROPERTY_NAME = 
"grails.views.gsp.modelrecording"
-    public static final boolean ENABLED = 
Boolean.getBoolean(CONFIG_SYSTEM_PROPERTY_NAME)
+    public static final String CONFIG_SYSTEM_PROPERTY_NAME
+    public static final boolean ENABLED
+    static {
+        CONFIG_SYSTEM_PROPERTY_NAME = "grails.views.gsp.modelrecording"
+        ENABLED = Boolean.getBoolean(CONFIG_SYSTEM_PROPERTY_NAME)
+    }
     private static final ModelRecordingCache modelRecordingCache = new 
ModelRecordingCache()
     private ModelEntry modelEntry
 
@@ -186,7 +192,7 @@ class ModelEntry {
                             if 
(defaultTypeClass.isAssignableFrom(fieldTypeClass)) {
                                 cleanedFieldType = defaultType
                             }
-                        } catch (ignored) {
+                        } catch (e) {
                             // ignore
                         }
                     }
diff --git 
a/grails-gsp/grails-taglib/src/main/groovy/org/grails/taglib/AbstractTemplateVariableBinding.java
 
b/grails-gsp/grails-taglib/src/main/groovy/org/grails/taglib/AbstractTemplateVariableBinding.java
index 693e853b99..0a5bf32c47 100644
--- 
a/grails-gsp/grails-taglib/src/main/groovy/org/grails/taglib/AbstractTemplateVariableBinding.java
+++ 
b/grails-gsp/grails-taglib/src/main/groovy/org/grails/taglib/AbstractTemplateVariableBinding.java
@@ -1,30 +1,26 @@
 /*
- *  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
+ *  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
  *
- *      https://www.apache.org/licenses/LICENSE-2.0
+ *    https://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.
+ *  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.grails.taglib;
 
 import groovy.lang.Binding;
 
-import java.util.AbstractSet;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 
 /**
  * Abstract super class for GroovyPage bindings
@@ -181,7 +177,7 @@ public abstract class AbstractTemplateVariableBinding 
extends Binding {
     }
 
     protected static class BindingMapEntry implements Map.Entry {
-        private final AbstractTemplateVariableBinding binding;
+        private AbstractTemplateVariableBinding binding;
 
         private Object key;
         private Object value;
@@ -215,9 +211,10 @@ public abstract class AbstractTemplateVariableBinding 
extends Binding {
             if (obj == this) {
                 return true;
             }
-            if (!(obj instanceof Map.Entry other)) {
+            if (!(obj instanceof Map.Entry)) {
                 return false;
             }
+            Map.Entry other = (Map.Entry) obj;
             return
                     (getKey() == null ? other.getKey() == null : 
getKey().equals(other.getKey())) &&
                             (getValue() == null ? other.getValue() == null : 
getValue().equals(other.getValue()));
diff --git 
a/grails-gsp/grails-web-gsp/src/main/groovy/org/grails/web/gsp/GroovyPagesTemplateRenderer.java
 
b/grails-gsp/grails-web-gsp/src/main/groovy/org/grails/web/gsp/GroovyPagesTemplateRenderer.java
index dd3bc84c93..49832d5c17 100644
--- 
a/grails-gsp/grails-web-gsp/src/main/groovy/org/grails/web/gsp/GroovyPagesTemplateRenderer.java
+++ 
b/grails-gsp/grails-web-gsp/src/main/groovy/org/grails/web/gsp/GroovyPagesTemplateRenderer.java
@@ -1,18 +1,20 @@
 /*
- *  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
+ *  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
  *
- *      https://www.apache.org/licenses/LICENSE-2.0
+ *    https://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.
+ *  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.grails.web.gsp;
 
@@ -50,7 +52,6 @@ import org.springframework.util.ReflectionUtils;
 import java.io.IOException;
 import java.io.Writer;
 import java.lang.reflect.Method;
-import java.nio.charset.StandardCharsets;
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
@@ -61,7 +62,7 @@ import java.util.concurrent.ConcurrentMap;
 
 /**
  * Service that provides the actual implementation to RenderTagLib's render 
tag.
- * <p>
+ *
  * This is an internal Grails service and should not be used by plugins 
directly.
  * The implementation was moved from RenderTagLib, ported to Java and then 
refactored.
  *
@@ -73,7 +74,7 @@ import java.util.concurrent.ConcurrentMap;
 public class GroovyPagesTemplateRenderer implements InitializingBean {
     private GrailsConventionGroovyPageLocator groovyPageLocator;
     private GroovyPagesTemplateEngine groovyPagesTemplateEngine;
-    private final ConcurrentMap<String, CacheEntry<Template>> templateCache = 
new ConcurrentHashMap<>();
+    private ConcurrentMap<String,CacheEntry<Template>> templateCache = new 
ConcurrentHashMap<String,CacheEntry<Template>>();
     private Object scaffoldingTemplateGenerator;
     private Map<String, Collection<String>> scaffoldedActionMap;
     private Map<String, GrailsDomainClass> 
controllerToScaffoldedDomainClassMap;
@@ -157,23 +158,23 @@ public class GroovyPagesTemplateRenderer implements 
InitializingBean {
         return CacheEntry.getValue(templateCache, cacheKey, reloadEnabled ? 
GroovyPageMetaInfo.LASTMODIFIED_CHECK_INTERVAL : -1, null,
                 new Callable<CacheEntry<Template>>() {
                     public CacheEntry<Template> call() {
-                        return new CacheEntry<>() {
+                        return new CacheEntry<Template>() {
                             boolean allowCaching = cacheEnabled;
                             boolean neverExpire = false;
 
                             @Override
                             protected boolean hasExpired(long timeout, Object 
cacheRequestObject) {
-                                return !neverExpire && (!allowCaching || 
super.hasExpired(timeout, cacheRequestObject));
+                                return neverExpire ? false : (allowCaching ? 
super.hasExpired(timeout, cacheRequestObject) : true);
                             }
-
+                            
                             @Override
                             public boolean isInitialized() {
-                                return allowCaching && super.isInitialized();
+                                return allowCaching ? super.isInitialized() : 
false;
                             }
-
+                            
                             @Override
                             public void setValue(Template val) {
-                                if (allowCaching) {
+                                if(allowCaching) {
                                     super.setValue(val);
                                 }
                             }
@@ -229,7 +230,8 @@ public class GroovyPagesTemplateRenderer implements 
InitializingBean {
                 if (key == null && GrailsStringUtils.isBlank(var) && it != 
null) {
                     key = GrailsNameUtils.getPropertyName(it.getClass());
                 }
-                Map itmap = new LinkedHashMap<String, Object>(b);
+                Map itmap = new LinkedHashMap<String, Object>();
+                itmap.putAll(b);
                 if (GrailsStringUtils.isNotBlank(var)) {
                     itmap.put(var, it);
                 }
@@ -280,7 +282,7 @@ public class GroovyPagesTemplateRenderer implements 
InitializingBean {
                 }
                 FastStringWriter sw = new FastStringWriter();
                 ReflectionUtils.invokeMethod(generateViewMethod, 
scaffoldingTemplateGenerator, domainClass, scaffoldedtemplateName, sw);
-                t = groovyPagesTemplateEngine.createTemplate(new 
ByteArrayResource(sw.toString().getBytes(StandardCharsets.UTF_8), uri), false);
+                t = groovyPagesTemplateEngine.createTemplate(new 
ByteArrayResource(sw.toString().getBytes("UTF-8"), uri), false);
             }
         }
         return t;
diff --git 
a/grails-gsp/grails-web-jsp/src/main/groovy/org/grails/gsp/jsp/GroovyPagesPageContext.java
 
b/grails-gsp/grails-web-jsp/src/main/groovy/org/grails/gsp/jsp/GroovyPagesPageContext.java
index fd30f737d9..7c762ba45d 100644
--- 
a/grails-gsp/grails-web-jsp/src/main/groovy/org/grails/gsp/jsp/GroovyPagesPageContext.java
+++ 
b/grails-gsp/grails-web-jsp/src/main/groovy/org/grails/gsp/jsp/GroovyPagesPageContext.java
@@ -1,38 +1,30 @@
 /*
- *  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
+ *  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
  *
- *      https://www.apache.org/licenses/LICENSE-2.0
+ *    https://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.
+ *  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.grails.gsp.jsp;
 
 import groovy.lang.Binding;
 import jakarta.el.ELContext;
-import jakarta.servlet.GenericServlet;
-import jakarta.servlet.Servlet;
-import jakarta.servlet.ServletConfig;
-import jakarta.servlet.ServletContext;
-import jakarta.servlet.ServletException;
-import jakarta.servlet.ServletRequest;
-import jakarta.servlet.ServletResponse;
+import jakarta.servlet.*;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import jakarta.servlet.http.HttpSession;
-import jakarta.servlet.jsp.JspApplicationContext;
-import jakarta.servlet.jsp.JspContext;
-import jakarta.servlet.jsp.JspFactory;
-import jakarta.servlet.jsp.JspWriter;
-import jakarta.servlet.jsp.PageContext;
+import jakarta.servlet.jsp.*;
 import jakarta.servlet.jsp.tagext.BodyContent;
 import org.grails.gsp.GroovyPage;
 import org.grails.web.servlet.mvc.GrailsWebRequest;
@@ -41,14 +33,7 @@ import 
org.springframework.web.context.request.RequestContextHolder;
 
 import java.io.IOException;
 import java.io.Writer;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Deque;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Map;
-import java.util.NoSuchElementException;
+import java.util.*;
 
 /**
  * A JSP PageContext implementation for use with GSP.
@@ -107,7 +92,7 @@ public class GroovyPagesPageContext extends PageContext {
 
     void popWriter() {
         outStack.pop();
-        jspOut = outStack.peek();
+        jspOut = (JspWriter) outStack.peek();
         setCurrentOut();
     }
 
@@ -285,13 +270,13 @@ public class GroovyPagesPageContext extends PageContext {
     public Object getAttribute(String name, int scope) {
         Assert.notNull(name, "Attribute name cannot be null");
 
-        return switch (scope) {
-            case PAGE_SCOPE -> getAttribute(name);
-            case REQUEST_SCOPE -> request.getAttribute(name);
-            case SESSION_SCOPE -> request.getSession(true).getAttribute(name);
-            case APPLICATION_SCOPE -> servletContext.getAttribute(name);
-            default -> getAttribute(name);
-        };
+        switch (scope) {
+            case PAGE_SCOPE:        return getAttribute(name);
+            case REQUEST_SCOPE:     return request.getAttribute(name);
+            case SESSION_SCOPE:     return 
request.getSession(true).getAttribute(name);
+            case APPLICATION_SCOPE: return servletContext.getAttribute(name);
+            default:                return getAttribute(name);
+        }
     }
 
     @Override
diff --git 
a/grails-web-common/src/main/groovy/org/grails/web/json/JSONObject.java 
b/grails-web-common/src/main/groovy/org/grails/web/json/JSONObject.java
index 01782094ec..e7e554c852 100644
--- a/grails-web-common/src/main/groovy/org/grails/web/json/JSONObject.java
+++ b/grails-web-common/src/main/groovy/org/grails/web/json/JSONObject.java
@@ -30,12 +30,7 @@ import org.springframework.util.ClassUtils;
 
 import java.io.IOException;
 import java.io.Writer;
-import java.util.Collection;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 
 /**
  * A JSONObject is an unordered collection of name/value pairs. Its
@@ -97,7 +92,7 @@ public class JSONObject implements JSONElement, Map {
     private static boolean useStreamingJavascriptEncoder=false;
     static {
         try {
-            javascriptEncoder = (StreamingEncoder) 
ClassUtils.forName("grails.encoders.JSONEncoder", 
JSONObject.class.getClassLoader()).getDeclaredConstructor().newInstance();
+            javascriptEncoder = 
(StreamingEncoder)ClassUtils.forName("grails.encoders.JSONEncoder", 
JSONObject.class.getClassLoader()).newInstance();
             javascriptEncoderStateless = (EncodesToWriter)javascriptEncoder;
             useStreamingJavascriptEncoder = true;
         }
@@ -109,7 +104,7 @@ public class JSONObject implements JSONElement, Map {
     /**
      * The hash map where the JSONObject's properties are kept.
      */
-    private final HashMap myHashMap;
+    private HashMap myHashMap;
 
     /**
      * Construct an empty JSONObject.
@@ -130,8 +125,8 @@ public class JSONObject implements JSONElement, Map {
      */
     public JSONObject(JSONObject jo, String[] sa) throws JSONException {
         this();
-        for (String s : sa) {
-            putOpt(s, jo.opt(s));
+        for (int i = 0; i < sa.length; i += 1) {
+            putOpt(sa[i], jo.opt(sa[i]));
         }
     }
 
@@ -446,7 +441,7 @@ public class JSONObject implements JSONElement, Map {
         while (keys.hasNext()) {
             ja.put(keys.next());
         }
-        return ja.isEmpty() ? null : ja;
+        return ja.length() == 0 ? null : ja;
     }
 
     /**
@@ -484,11 +479,12 @@ public class JSONObject implements JSONElement, Map {
     static public String collectionToString(Collection c) {
         StringBuilder sb = new StringBuilder("[");
         boolean first = true;
-        for (Object o : c) {
+        Iterator iterator = c.iterator();
+        while(iterator.hasNext()) {
             if (!first) {
                 sb.append(',');
             }
-            sb.append(valueToString(o));
+            sb.append(valueToString(iterator.next()));
             first = false;
         }
         sb.append("]");
@@ -566,7 +562,7 @@ public class JSONObject implements JSONElement, Map {
         try {
             Object o = opt(key);
             return o instanceof Number ? ((Number) o).doubleValue() :
-                    Double.parseDouble((String) o);
+                    Double.valueOf((String) o);
         } catch (Exception e) {
             return defaultValue;
         }
@@ -805,7 +801,7 @@ public class JSONObject implements JSONElement, Map {
      * @return A String correctly formatted for insertion in a JSON text.
      */
     public static String quote(String string) {
-        if (string == null || string.isEmpty()) {
+        if (string == null || string.length() == 0) {
             return "\"\"";
         }
 
@@ -850,7 +846,7 @@ public class JSONObject implements JSONElement, Map {
                 default:
                     if (c < ' ') {
                         t = "000" + Integer.toHexString(c);
-                        sb.append("\\u").append(t.substring(t.length() - 4));
+                        sb.append("\\u" + t.substring(t.length() - 4));
                     } else {
                         sb.append(c);
                     }
@@ -904,7 +900,7 @@ public class JSONObject implements JSONElement, Map {
      * @throws JSONException If any of the values are non-finite numbers.
      */
     public JSONArray toJSONArray(JSONArray names) throws JSONException {
-        if (names == null || names.isEmpty()) {
+        if (names == null || names.length() == 0) {
             return null;
         }
         JSONArray ja = new JSONArray();
@@ -1038,7 +1034,7 @@ public class JSONObject implements JSONElement, Map {
      * @throws JSONException If the value is or contains an invalid number.
      */
     static String valueToString(Object value) throws JSONException {
-        if (value == null) {
+        if (value == null || value.equals(null)) {
             return "null";
         }
         if (value instanceof Number) {
@@ -1057,7 +1053,7 @@ public class JSONObject implements JSONElement, Map {
     }
 
     static void writeValue(Writer writer, Object value) throws IOException {
-        if (value == null) {
+        if (value == null || value.equals(null)) {
             writer.write("null");
         } else if (value instanceof Number) {
             writeNumber(writer, (Number) value);
@@ -1124,7 +1120,7 @@ public class JSONObject implements JSONElement, Map {
      */
     static String valueToString(Object value, int indentFactor, int indent)
             throws JSONException {
-        if (value == null) {
+        if (value == null || value.equals(null)) {
             return "null";
         }
         if (value instanceof Number) {
@@ -1153,14 +1149,14 @@ public class JSONObject implements JSONElement, Map {
      * Warning: This method assumes that the data structure is acyclical.
      *
      * @return The writer.
-     * @throws JSONException if an I/O exception occurs using the writer
+     * @throws JSONException
      */
     public Writer write(Writer writer) throws JSONException {
         try {
             boolean notFirst = false;
             writer.write('{');
-            for (Object o : myHashMap.entrySet()) {
-                Entry entry = (Entry) o;
+            for(Iterator it = myHashMap.entrySet().iterator(); it.hasNext();) {
+                Map.Entry entry = (Entry)it.next();
                 if (notFirst) {
                     writer.write(',');
                 }
@@ -1231,7 +1227,9 @@ public class JSONObject implements JSONElement, Map {
 
         JSONObject that = (JSONObject) o;
 
-        return myHashMap != null ? myHashMap.equals(that.myHashMap) : 
that.myHashMap == null;
+        if (myHashMap != null ? !myHashMap.equals(that.myHashMap) : 
that.myHashMap != null) return false;
+
+        return true;
     }
 
     @Override
diff --git 
a/grails-web-core/src/main/groovy/grails/web/servlet/context/GrailsWebApplicationContext.java
 
b/grails-web-core/src/main/groovy/grails/web/servlet/context/GrailsWebApplicationContext.java
index 76fbe0523f..04916cf41e 100644
--- 
a/grails-web-core/src/main/groovy/grails/web/servlet/context/GrailsWebApplicationContext.java
+++ 
b/grails-web-core/src/main/groovy/grails/web/servlet/context/GrailsWebApplicationContext.java
@@ -1,26 +1,30 @@
 /*
- *  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
+ *  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
  *
- *      https://www.apache.org/licenses/LICENSE-2.0
+ *    https://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.
+ *  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 grails.web.servlet.context;
 
-import grails.core.GrailsApplication;
 import grails.spring.BeanBuilder;
-import grails.web.servlet.context.support.GrailsEnvironment;
+
 import jakarta.servlet.ServletConfig;
 import jakarta.servlet.ServletContext;
+
+import grails.core.GrailsApplication;
+import grails.web.servlet.context.support.GrailsEnvironment;
 import org.grails.spring.GrailsApplicationContext;
 import org.springframework.beans.BeansException;
 import 
org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@@ -174,11 +178,13 @@ public class GrailsWebApplicationContext extends 
GrailsApplicationContext
 
     @Override
     protected void prepareBeanFactory(ConfigurableListableBeanFactory 
beanFactory) {
-        for (String configLocation : configLocations) {
-            BeanBuilder beanBuilder = new BeanBuilder(getParent(), 
getClassLoader());
-            final ServletContextResource resource = new 
ServletContextResource(getServletContext(), configLocation);
-            beanBuilder.loadBeans(resource);
-            beanBuilder.registerBeans(this);
+        if (configLocations.length > 0) {
+            for (String configLocation : configLocations) {
+                BeanBuilder beanBuilder = new 
BeanBuilder(getParent(),getClassLoader());
+                final ServletContextResource resource = new 
ServletContextResource(getServletContext(), configLocation);
+                beanBuilder.loadBeans(resource);
+                beanBuilder.registerBeans(this);
+            }
         }
         super.prepareBeanFactory(beanFactory);
     }
diff --git 
a/grails-web-databinding/src/main/groovy/grails/web/databinding/DataBindingUtils.java
 
b/grails-web-databinding/src/main/groovy/grails/web/databinding/DataBindingUtils.java
index 76d078b312..f45424747a 100644
--- 
a/grails-web-databinding/src/main/groovy/grails/web/databinding/DataBindingUtils.java
+++ 
b/grails-web-databinding/src/main/groovy/grails/web/databinding/DataBindingUtils.java
@@ -1,34 +1,44 @@
 /*
- *  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
+ *  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
  *
- *      https://www.apache.org/licenses/LICENSE-2.0
+ *    https://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.
+ *  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 grails.web.databinding;
 
-import grails.core.GrailsApplication;
 import grails.databinding.CollectionDataBindingSource;
 import grails.databinding.DataBinder;
 import grails.databinding.DataBindingSource;
 import grails.util.Environment;
 import grails.util.Holders;
 import grails.validation.ValidationErrors;
-import grails.web.mime.MimeType;
-import grails.web.mime.MimeTypeResolver;
-import grails.web.mime.MimeTypeUtils;
 import groovy.lang.GroovySystem;
 import groovy.lang.MetaClass;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
 import jakarta.servlet.ServletRequest;
+
+import grails.core.GrailsApplication;
+import grails.web.mime.MimeType;
+import grails.web.mime.MimeTypeResolver;
+import grails.web.mime.MimeTypeUtils;
+
 import org.grails.core.exceptions.GrailsConfigurationException;
 import org.grails.datastore.mapping.model.PersistentEntity;
 import org.grails.datastore.mapping.model.PersistentProperty;
@@ -43,15 +53,6 @@ import org.springframework.validation.BindingResult;
 import org.springframework.validation.FieldError;
 import org.springframework.validation.ObjectError;
 
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
 /**
  * Utility methods to perform data binding from Grails objects.
  *
@@ -63,7 +64,7 @@ public class DataBindingUtils {
 
     public static final String DATA_BINDER_BEAN_NAME = "grailsWebDataBinder";
     private static final String BLANK = "";
-    private static final Map<Class, List> CLASS_TO_BINDING_INCLUDE_LIST = new 
ConcurrentHashMap<>();
+    private static final Map<Class, List> CLASS_TO_BINDING_INCLUDE_LIST = new 
ConcurrentHashMap<Class, List>();
 
     /**
      * Associations both sides of any bidirectional relationships found in the 
object and source map to bind
@@ -84,7 +85,7 @@ public class DataBindingUtils {
             }
             PersistentProperty prop = 
persistentEntity.getPropertyByName(propertyName);
 
-            if (prop instanceof OneToOne && ((OneToOne) 
prop).isBidirectional()) {
+            if (prop != null && prop instanceof OneToOne && ((OneToOne) 
prop).isBidirectional()) {
                 Object val = source.get(key);
                 PersistentProperty otherSide = ((OneToOne) 
prop).getInverseSide();
                 if (val != null && otherSide != null) {
@@ -120,17 +121,19 @@ public class DataBindingUtils {
                 includeList = CLASS_TO_BINDING_INCLUDE_LIST.get(objectClass);
             } else {
                 final Field whiteListField = 
objectClass.getDeclaredField(DefaultASTDatabindingHelper.DEFAULT_DATABINDING_WHITELIST);
-                if ((whiteListField.getModifiers() & Modifier.STATIC) != 0) {
-                    final Object whiteListValue = 
whiteListField.get(objectClass);
-                    if (whiteListValue instanceof List) {
-                        includeList = (List) whiteListValue;
+                if (whiteListField != null) {
+                    if ((whiteListField.getModifiers() & Modifier.STATIC) != 
0) {
+                         final Object whiteListValue = 
whiteListField.get(objectClass);
+                         if (whiteListValue instanceof List) {
+                             includeList = (List)whiteListValue;
+                         }
                     }
                 }
                 if (!Environment.getCurrent().isReloadEnabled()) {
                     CLASS_TO_BINDING_INCLUDE_LIST.put(objectClass, 
includeList);
                 }
             }
-        } catch (Exception ignored) {
+        } catch (Exception e) {
         }
         return includeList;
     }
@@ -250,7 +253,8 @@ public class DataBindingUtils {
         if (entity != null && bindingResult != null) {
             BindingResult newResult = new ValidationErrors(object);
             for (Object error : bindingResult.getAllErrors()) {
-                if (error instanceof FieldError fieldError) {
+                if (error instanceof FieldError) {
+                    FieldError fieldError = (FieldError)error;
                     final boolean isBlank = 
BLANK.equals(fieldError.getRejectedValue());
                     if (!isBlank) {
                         newResult.addError(fieldError);
@@ -285,7 +289,8 @@ public class DataBindingUtils {
 
     protected static String[] getMessageCodes(String messageCode,
             Class objectType) {
-        return new String[]{objectType.getName() + "." + messageCode, 
messageCode};
+        String[] codes = {objectType.getName() + "." + messageCode, 
messageCode};
+        return codes;
     }
 
     public static DataBindingSourceRegistry 
getDataBindingSourceRegistry(GrailsApplication grailsApplication) {
diff --git 
a/grails-web-mvc/src/main/groovy/org/grails/web/errors/GrailsExceptionResolver.java
 
b/grails-web-mvc/src/main/groovy/org/grails/web/errors/GrailsExceptionResolver.java
index 31693e1c35..23c4faa98c 100644
--- 
a/grails-web-mvc/src/main/groovy/org/grails/web/errors/GrailsExceptionResolver.java
+++ 
b/grails-web-mvc/src/main/groovy/org/grails/web/errors/GrailsExceptionResolver.java
@@ -1,46 +1,53 @@
 /*
- *  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
+ *  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
  *
- *      https://www.apache.org/licenses/LICENSE-2.0
+ *    https://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.
+ *  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.grails.web.errors;
 
 import grails.config.Config;
 import grails.config.Settings;
-import grails.core.GrailsApplication;
-import grails.core.support.GrailsApplicationAware;
 import grails.util.Environment;
-import grails.web.mapping.UrlMappingInfo;
-import grails.web.mapping.UrlMappingsHolder;
-import grails.web.mapping.exceptions.UrlMappingException;
+
+import java.io.IOException;
+import java.util.*;
+
 import jakarta.servlet.ServletContext;
 import jakarta.servlet.ServletException;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+
+import grails.web.mapping.exceptions.UrlMappingException;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.codehaus.groovy.control.CompilationFailedException;
-import org.codehaus.groovy.runtime.InvokerInvocationException;
-import org.grails.core.exceptions.GrailsRuntimeException;
-import org.grails.exceptions.ExceptionUtils;
+import grails.core.GrailsApplication;
 import org.grails.exceptions.reporting.DefaultStackTraceFilterer;
+import org.grails.core.exceptions.GrailsRuntimeException;
 import org.grails.exceptions.reporting.StackTraceFilterer;
+import grails.core.support.GrailsApplicationAware;
+import grails.web.mapping.UrlMappingInfo;
+import org.grails.exceptions.ExceptionUtils;
 import org.grails.web.mapping.DefaultUrlMappingInfo;
 import org.grails.web.mapping.UrlMappingUtils;
-import org.grails.web.servlet.mvc.exceptions.GrailsMVCException;
+import grails.web.mapping.UrlMappingsHolder;
 import org.grails.web.util.GrailsApplicationAttributes;
+import org.grails.web.servlet.mvc.exceptions.GrailsMVCException;
 import org.grails.web.util.WebUtils;
+import org.codehaus.groovy.runtime.InvokerInvocationException;
 import org.springframework.beans.BeanUtils;
 import org.springframework.web.context.ServletContextAware;
 import org.springframework.web.servlet.ModelAndView;
@@ -48,13 +55,6 @@ import org.springframework.web.servlet.View;
 import org.springframework.web.servlet.ViewResolver;
 import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
 
-import java.io.IOException;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
 /**
  * Wraps any runtime exceptions with a GrailsWrappedException instance.
  *
@@ -65,7 +65,7 @@ public class GrailsExceptionResolver extends 
SimpleMappingExceptionResolver impl
     public static final String EXCEPTION_ATTRIBUTE = 
WebUtils.EXCEPTION_ATTRIBUTE;
 
     protected static final Log LOG = 
LogFactory.getLog(GrailsExceptionResolver.class);
-    protected static final String LINE_SEPARATOR = System.lineSeparator();
+    protected static final String LINE_SEPARATOR = 
System.getProperty("line.separator");
 
     protected ServletContext servletContext;
     protected GrailsApplication grailsApplication;
@@ -283,7 +283,7 @@ public class GrailsExceptionResolver extends 
SimpleMappingExceptionResolver impl
 
             if (params.hasMoreElements()) {
                 String param;
-                String[] values;
+                String values[];
                 int i;
 
                 sb.append(" - parameters:");
diff --git 
a/grails-web-url-mappings/src/main/groovy/org/grails/web/mapping/RegexUrlMapping.java
 
b/grails-web-url-mappings/src/main/groovy/org/grails/web/mapping/RegexUrlMapping.java
index 147f670474..a98ba3c92a 100644
--- 
a/grails-web-url-mappings/src/main/groovy/org/grails/web/mapping/RegexUrlMapping.java
+++ 
b/grails-web-url-mappings/src/main/groovy/org/grails/web/mapping/RegexUrlMapping.java
@@ -1,18 +1,20 @@
 /*
- *  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
+ *  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
  *
- *      https://www.apache.org/licenses/LICENSE-2.0
+ *    https://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.
+ *  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.grails.web.mapping;
 
@@ -36,19 +38,10 @@ import org.springframework.validation.Errors;
 import org.springframework.validation.MapBindingResult;
 import org.springframework.web.context.request.RequestContextHolder;
 
-import java.io.Serial;
 import java.io.UnsupportedEncodingException;
 import java.net.URI;
 import java.net.URLEncoder;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.regex.PatternSyntaxException;
@@ -73,7 +66,7 @@ public class RegexUrlMapping extends AbstractUrlMapping {
 
     public static final String FORMAT_PARAMETER = "format";
     private Pattern[] patterns;
-    private final Map<Integer, List<Pattern>> patternByTokenCount = new 
HashMap<>();
+    private Map<Integer, List<Pattern>> patternByTokenCount = new 
HashMap<Integer, List<Pattern>>();
     private UrlMappingData urlData;
     private static final String DEFAULT_ENCODING = "UTF-8";
     private static final Logger LOG = 
LoggerFactory.getLogger(RegexUrlMapping.class);
@@ -132,7 +125,11 @@ public class RegexUrlMapping extends AbstractUrlMapping {
         for (int i = 0; i < urls.length; i++) {
             String url = urls[i];
             Integer slashCount = 
org.springframework.util.StringUtils.countOccurrencesOf(url, "/");
-            List<Pattern> tokenCountPatterns = 
patternByTokenCount.computeIfAbsent(slashCount, k -> new ArrayList<>());
+            List<Pattern> tokenCountPatterns = 
patternByTokenCount.get(slashCount);
+            if (tokenCountPatterns == null) {
+                tokenCountPatterns = new ArrayList<>();
+                patternByTokenCount.put(slashCount, tokenCountPatterns);
+            }
 
             Pattern pattern = convertToRegex(url);
             if (pattern == null) {
@@ -198,8 +195,9 @@ public class RegexUrlMapping extends AbstractUrlMapping {
     }
 
     private void setNullable(ConstrainedProperty constraint) {
-        if (!constraint.isNullable()) {
-            
constraint.applyConstraint(ConstrainedProperty.NULLABLE_CONSTRAINT, true);
+        ConstrainedProperty constrainedProperty = constraint;
+        if(!constrainedProperty.isNullable()) {
+               
constrainedProperty.applyConstraint(ConstrainedProperty.NULLABLE_CONSTRAINT, 
true);
         }
     }
 
@@ -221,13 +219,13 @@ public class RegexUrlMapping extends AbstractUrlMapping {
             int lastSlash = pattern.lastIndexOf('/');
 
             String urlRoot = lastSlash > -1 ? pattern.substring(0, lastSlash) 
: pattern;
-            String urlEnd = lastSlash > -1 ? pattern.substring(lastSlash) : "";
+            String urlEnd = lastSlash > -1 ? pattern.substring(lastSlash, 
pattern.length()) : "";
 
             // Now replace "*" with "[^/]" and "**" with ".*".
             pattern = "^" + urlRoot
                     .replace("(\\.(*))", "(\\.[^/]+)?")
-                    .replaceAll("([^*])\\*([^*])", "$1[^/]+?$2")
-                    .replaceAll("([^*])\\*$", "$1[^/]+?")
+                    .replaceAll("([^\\*])\\*([^\\*])", "$1[^/]+?$2")
+                    .replaceAll("([^\\*])\\*$", "$1[^/]+?")
                     .replaceAll("\\*\\*", ".*");
 
             if("/(*)(\\.(*))".equals(urlEnd)) {
@@ -238,11 +236,11 @@ public class RegexUrlMapping extends AbstractUrlMapping {
             } else {
                 pattern += urlEnd
                         .replace("(\\.(*))", "(\\.[^/]+)?")
-                        .replaceAll("([^*])\\*([^*])", "$1[^/]+?$2")
-                        .replaceAll("([^*])\\*$", "$1[^/]+?")
+                        .replaceAll("([^\\*])\\*([^\\*])", "$1[^/]+?$2")
+                        .replaceAll("([^\\*])\\*$", "$1[^/]+?")
                         .replaceAll("\\*\\*", ".*")
-                        .replaceAll("\\(\\[\\^/]\\+\\)\\\\\\.", 
"([^/.]+?)\\\\.")
-                        .replaceAll("\\(\\[\\^/]\\+\\)\\?\\\\\\.", 
"([^/.]+?)?\\\\.")
+                        .replaceAll("\\(\\[\\^\\/\\]\\+\\)\\\\\\.", 
"([^/.]+?)\\\\.")
+                        .replaceAll("\\(\\[\\^\\/\\]\\+\\)\\?\\\\\\.", 
"([^/.]+?)\\?\\\\.")
                 ;
             }
             pattern += "/??$";
@@ -352,7 +350,7 @@ public class RegexUrlMapping extends AbstractUrlMapping {
             }
             m = DOUBLE_WILDCARD_PATTERN.matcher(token);
             if (m.find()) {
-                StringBuilder buf = new StringBuilder();
+                StringBuffer buf = new StringBuffer();
                 do {
                     ConstrainedProperty prop = constraints[paramIndex++];
                     String propName = prop.getPropertyName();
@@ -376,7 +374,7 @@ public class RegexUrlMapping extends AbstractUrlMapping {
 
                 try {
                     String v = buf.toString();
-                    if (v.contains(SLASH) && 
CAPTURED_DOUBLE_WILDCARD.equals(token)) {
+                    if (v.indexOf(SLASH) > -1 && 
CAPTURED_DOUBLE_WILDCARD.equals(token)) {
                         // individually URL encode path segments
                         if (v.startsWith(SLASH)) {
                             // get rid of leading slash
@@ -386,7 +384,8 @@ public class RegexUrlMapping extends AbstractUrlMapping {
                         for (String segment : segs) {
                             uri.append(SLASH).append(encode(segment, 
encoding));
                         }
-                    } else if (!v.isEmpty()) {
+                    }
+                    else if (v.length() > 0) {
                         // original behavior
                         uri.append(SLASH).append(encode(v, encoding));
                     }
@@ -407,7 +406,7 @@ public class RegexUrlMapping extends AbstractUrlMapping {
         populateParameterList(paramValues, encoding, uri, usedParams);
 
         if (LOG.isDebugEnabled()) {
-            LOG.debug("Created reverse URL mapping [" + uri + "] for 
parameters [" + paramValues + "]");
+            LOG.debug("Created reverse URL mapping [" + uri.toString() + "] 
for parameters [" + paramValues + "]");
         }
         return uri.toString();
     }
@@ -548,7 +547,8 @@ public class RegexUrlMapping extends AbstractUrlMapping {
                     uri.append(AMPERSAND);
                 }
                 Object value = paramValues.get(name);
-                if (value instanceof Collection multiValues) {
+                if (value != null && value instanceof Collection) {
+                    Collection multiValues = (Collection) value;
                     for (Iterator j = multiValues.iterator(); j.hasNext();) {
                         Object o = j.next();
                         appendValueToURI(encoding, uri, name, o);
@@ -595,7 +595,7 @@ public class RegexUrlMapping extends AbstractUrlMapping {
         Map params = new HashMap();
         Errors errors = new MapBindingResult(params, "urlMapping");
         int groupCount = m.groupCount();
-        String lastGroup;
+        String lastGroup = null;
         for (int i = 0; i < groupCount; i++) {
             lastGroup = m.group(i + 1);
             // if null optional.. ignore
@@ -614,7 +614,7 @@ public class RegexUrlMapping extends AbstractUrlMapping {
                     }
                 }
                 // if the format is specified but the value is empty, ignore it
-                if (!(FORMAT_PARAMETER.equals(propertyName) && 
!GrailsStringUtils.hasLength(lastGroup))) {
+                if (!(FORMAT_PARAMETER.equals(propertyName) && 
GrailsStringUtils.isEmpty(lastGroup))) {
                     params.put(propertyName, lastGroup);
                 }
                 break;
@@ -638,7 +638,7 @@ public class RegexUrlMapping extends AbstractUrlMapping {
                         lastGroup = lastGroup.substring(1);
                     }
                     // if the format is specified but the value is empty, 
ignore it
-                    if (!(FORMAT_PARAMETER.equals(propertyName) && 
!GrailsStringUtils.hasLength(lastGroup))) {
+                    if (!(FORMAT_PARAMETER.equals(propertyName) && 
GrailsStringUtils.isEmpty(lastGroup))) {
                         params.put(propertyName, lastGroup);
                     }
                 }
@@ -703,7 +703,6 @@ public class RegexUrlMapping extends AbstractUrlMapping {
         for (ConstrainedProperty constraint : constraints) {
             if (constraint.getPropertyName().equals(name)) {
                 return new Closure(this) {
-                    @Serial
                     private static final long serialVersionUID = 
-2404119898659287216L;
 
                     @Override
@@ -741,12 +740,14 @@ public class RegexUrlMapping extends AbstractUrlMapping {
      * @return a negative integer, zero, or a positive integer as this {@link 
UrlMapping} is less than, equal to, or greater than the specified {@link 
UrlMapping}.
      */
     public int compareTo(Object o) {
-        if (!(o instanceof UrlMapping other)) {
+        if (!(o instanceof UrlMapping)) {
             throw new IllegalArgumentException("Cannot compare with Object [" 
+ o + "]. It is not an instance of UrlMapping!");
         }
 
         if (equals(o)) return 0;
 
+        UrlMapping other = (UrlMapping) o;
+
         // this wild card count
         final int thisStaticTokenCount = getStaticTokenCount(this);
         final int thisSingleWildcardCount = getSingleWildcardCount(this);
@@ -769,27 +770,27 @@ public class RegexUrlMapping extends AbstractUrlMapping {
         }
         else if(isThisRoot) {
             if(LOG.isDebugEnabled()) {
-                LOG.debug("Mapping [{}] has a higher precedence than [{}] 
because it is the root", this, other);
+                LOG.debug("Mapping [{}] has a higher precedence than [{}] 
because it is the root", this.toString(), other.toString());
             }
             return 1;
         }
         else if(isThatRoot) {
             if(LOG.isDebugEnabled()) {
-                LOG.debug("Mapping [{}] has a lower precedence than [{}] 
because the latter is the root", this, other);
+                LOG.debug("Mapping [{}] has a lower precedence than [{}] 
because the latter is the root", this.toString(), other.toString());
             }
             return -1;
         }
 
         if (otherStaticTokenCount == 0 && thisStaticTokenCount > 0) {
             if(LOG.isDebugEnabled()) {
-                LOG.debug("Mapping [{}] has a higher precedence than [{}] 
because it has more path tokens", this, other);
+                LOG.debug("Mapping [{}] has a higher precedence than [{}] 
because it has more path tokens", this.toString(), other.toString());
             }
             return 1;
         }
 
         if (thisStaticTokenCount == 0 && otherStaticTokenCount > 0) {
             if(LOG.isDebugEnabled()) {
-                LOG.debug("Mapping [{}] has a lower precedence than [{}] 
because it has fewer path tokens", this, other);
+                LOG.debug("Mapping [{}] has a lower precedence than [{}] 
because it has fewer path tokens", this.toString(), other.toString());
             }
             return -1;
         }
@@ -799,13 +800,13 @@ public class RegexUrlMapping extends AbstractUrlMapping {
 
         if (otherStaticAndWildcardTokenCount==0 && 
thisStaticAndWildcardTokenCount>0) {
             if(LOG.isDebugEnabled()) {
-                LOG.debug("Mapping [{}] has a higher precedence than [{}] 
because it has more path tokens [{} vs {}]", this, other, 
thisStaticAndWildcardTokenCount, otherStaticAndWildcardTokenCount);
+                LOG.debug("Mapping [{}] has a higher precedence than [{}] 
because it has more path tokens [{} vs {}]", this.toString(), other.toString(), 
thisStaticAndWildcardTokenCount, otherStaticAndWildcardTokenCount);
             }
             return 1;
         }
         if (thisStaticAndWildcardTokenCount==0 && 
otherStaticAndWildcardTokenCount>0) {
             if(LOG.isDebugEnabled()) {
-                LOG.debug("Mapping [{}] has a higher precedence than [{}] 
because the latter has more path tokens [{} vs {}]", this, other, 
thisStaticAndWildcardTokenCount, otherStaticAndWildcardTokenCount);
+                LOG.debug("Mapping [{}] has a higher precedence than [{}] 
because the latter has more path tokens [{} vs {}]", this.toString(), 
other.toString(), thisStaticAndWildcardTokenCount, 
otherStaticAndWildcardTokenCount);
             }
             return -1;
         }
@@ -813,13 +814,13 @@ public class RegexUrlMapping extends AbstractUrlMapping {
         final int staticDiff = thisStaticTokenCount - otherStaticTokenCount;
         if (staticDiff < 0 && !otherHasWildCards) {
             if(LOG.isDebugEnabled()) {
-                LOG.debug("Mapping [{}] has a lower precedence than [{}] 
because the latter has more concrete path tokens [{} vs {}]", this, other, 
thisStaticTokenCount, otherStaticTokenCount);
+                LOG.debug("Mapping [{}] has a lower precedence than [{}] 
because the latter has more concrete path tokens [{} vs {}]", this.toString(), 
other.toString(), thisStaticTokenCount, otherStaticTokenCount);
             }
             return -1;
         }
         else if(staticDiff > 0 && !hasWildCards) {
             if(LOG.isDebugEnabled()) {
-                LOG.debug("Mapping [{}] has a higher precedence than [{}] 
because it has more concrete path tokens [{} vs {}]", this, other, 
thisStaticTokenCount, otherStaticTokenCount);
+                LOG.debug("Mapping [{}] has a higher precedence than [{}] 
because it has more concrete path tokens [{} vs {}]", this.toString(), 
other.toString(), thisStaticTokenCount, otherStaticTokenCount);
             }
             return 1;
         }
@@ -829,7 +830,7 @@ public class RegexUrlMapping extends AbstractUrlMapping {
         final int thisTokensLength = thisTokens.length;
         final int otherTokensLength = otherTokens.length;
 
-        int greaterLength = Math.max(thisTokensLength, otherTokensLength);
+        int greaterLength = thisTokensLength > otherTokensLength ? 
thisTokensLength : otherTokensLength;
         for (int i = 0; i < greaterLength; i++) {
             final boolean thisHasMoreTokens = i < thisTokensLength;
             final boolean otherHasMoreTokens = i < otherTokensLength;
@@ -838,13 +839,13 @@ public class RegexUrlMapping extends AbstractUrlMapping {
             boolean otherTokenIsWildcard = !otherHasMoreTokens || 
isSingleWildcard(otherTokens[i]);
             if (thisTokenIsWildcard && !otherTokenIsWildcard) {
                 if(LOG.isDebugEnabled()) {
-                    LOG.debug("Mapping [{}] has a lower precedence than [{}] 
because the latter contains more concrete tokens", this, other);
+                    LOG.debug("Mapping [{}] has a lower precedence than [{}] 
because the latter contains more concrete tokens", this.toString(), 
other.toString());
                 }
                 return -1;
             }
             if (!thisTokenIsWildcard && otherTokenIsWildcard) {
                 if(LOG.isDebugEnabled()) {
-                    LOG.debug("Mapping [{}] has a higher precedence than [{}] 
because it contains more concrete tokens", this, other);
+                    LOG.debug("Mapping [{}] has a higher precedence than [{}] 
because it contains more concrete tokens", this.toString(), other.toString());
                 }
                 return 1;
             }
@@ -854,10 +855,10 @@ public class RegexUrlMapping extends AbstractUrlMapping {
         if (doubleWildcardDiff != 0) {
             if(LOG.isDebugEnabled()) {
                 if(doubleWildcardDiff > 0) {
-                    LOG.debug("Mapping [{}] has a higher precedence than [{}] 
due containing more double wild cards [{} vs. {}]", this, other, 
thisDoubleWildcardCount, otherDoubleWildcardCount);
+                    LOG.debug("Mapping [{}] has a higher precedence than [{}] 
due containing more double wild cards [{} vs. {}]", this.toString(), 
other.toString(), thisDoubleWildcardCount, otherDoubleWildcardCount);
                 }
                 else if(doubleWildcardDiff < 0) {
-                    LOG.debug("Mapping [{}] has a lower precedence than [{}] 
due to the latter containing more double wild cards [{} vs. {}]", this, other, 
thisDoubleWildcardCount, otherDoubleWildcardCount);
+                    LOG.debug("Mapping [{}] has a lower precedence than [{}] 
due to the latter containing more double wild cards [{} vs. {}]", 
this.toString(), other.toString(), thisDoubleWildcardCount, 
otherDoubleWildcardCount);
                 }
             }
             return doubleWildcardDiff;
@@ -867,10 +868,10 @@ public class RegexUrlMapping extends AbstractUrlMapping {
         if (singleWildcardDiff != 0) {
             if(LOG.isDebugEnabled()) {
                 if(singleWildcardDiff > 0) {
-                    LOG.debug("Mapping [{}] has a higher precedence than [{}] 
because it contains more single wild card matches [{} vs. {}]", this, other, 
thisSingleWildcardCount, otherSingleWildcardCount);
+                    LOG.debug("Mapping [{}] has a higher precedence than [{}] 
because it contains more single wild card matches [{} vs. {}]", 
this.toString(), other.toString(), thisSingleWildcardCount, 
otherSingleWildcardCount);
                 }
                 else if(singleWildcardDiff < 0) {
-                    LOG.debug("Mapping [{}] has a lower precedence than [{}] 
due to the latter containing more single wild card matches[{} vs. {}]", this, 
other, thisSingleWildcardCount, otherSingleWildcardCount);
+                    LOG.debug("Mapping [{}] has a lower precedence than [{}] 
due to the latter containing more single wild card matches[{} vs. {}]", 
this.toString(), other.toString(), thisSingleWildcardCount, 
otherSingleWildcardCount);
                 }
             }
             return singleWildcardDiff;
@@ -882,10 +883,10 @@ public class RegexUrlMapping extends AbstractUrlMapping {
         if (constraintDiff != 0) {
             if(LOG.isDebugEnabled()) {
                 if(constraintDiff > 0) {
-                    LOG.debug("Mapping [{}] has a higher precedence than [{}] 
since it defines more constraints [{} vs. {}]", this, other, 
thisConstraintCount, thatConstraintCount);
+                    LOG.debug("Mapping [{}] has a higher precedence than [{}] 
since it defines more constraints [{} vs. {}]", this.toString(), 
other.toString(), thisConstraintCount, thatConstraintCount);
                 }
                 else if(constraintDiff < 0) {
-                    LOG.debug("Mapping [{}] has a lower precedence than [{}] 
since the latter defines more constraints [{} vs. {}]", this, other, 
thisConstraintCount, thatConstraintCount);
+                    LOG.debug("Mapping [{}] has a lower precedence than [{}] 
since the latter defines more constraints [{} vs. {}]", this.toString(), 
other.toString(), thisConstraintCount, thatConstraintCount);
                 }
             }
             return constraintDiff;
@@ -895,10 +896,10 @@ public class RegexUrlMapping extends AbstractUrlMapping {
         if(allDiff != 0) {
             if(LOG.isDebugEnabled()) {
                 if(allDiff > 0) {
-                    LOG.debug("Mapping [{}] has a higher precedence than [{}] 
due to the overall diff", this, other);
+                    LOG.debug("Mapping [{}] has a higher precedence than [{}] 
due to the overall diff", this.toString(), other.toString());
                 }
                 else if(allDiff < 0) {
-                    LOG.debug("Mapping [{}] has a lower precedence than [{}] 
due to the overall diff", this, other);
+                    LOG.debug("Mapping [{}] has a lower precedence than [{}] 
due to the overall diff", this.toString(), other.toString());
                 }
             }
             return allDiff;
@@ -911,13 +912,13 @@ public class RegexUrlMapping extends AbstractUrlMapping {
         }
         else if(thisVersion.equals(ANY_VERSION) && 
!thatVersion.equals(ANY_VERSION)) {
             if(LOG.isDebugEnabled()) {
-                LOG.debug("Mapping [{}] has a lower precedence than [{}] due 
to version precedence [{} vs {}]", this, other, thisVersion, thatVersion);
+                LOG.debug("Mapping [{}] has a lower precedence than [{}] due 
to version precedence [{} vs {}]", this.toString(), other.toString(), 
thisVersion, thatVersion);
             }
             return -1;
         }
         else if(!thisVersion.equals(ANY_VERSION) && 
thatVersion.equals(ANY_VERSION)) {
             if(LOG.isDebugEnabled()) {
-                LOG.debug("Mapping [{}] has a higher precedence than [{}] due 
to version precedence [{} vs {}]", this, other, thisVersion, thatVersion);
+                LOG.debug("Mapping [{}] has a higher precedence than [{}] due 
to version precedence [{} vs {}]", this.toString(), other.toString(), 
thisVersion, thatVersion);
             }
             return 1;
         }
@@ -926,13 +927,13 @@ public class RegexUrlMapping extends AbstractUrlMapping {
 
             if(i > 0) {
                 if(LOG.isDebugEnabled()) {
-                    LOG.debug("Mapping [{}] has a higher precedence than [{}] 
due to version precedence [{} vs. {}]", this, other, thisVersion, thatVersion);
+                    LOG.debug("Mapping [{}] has a higher precedence than [{}] 
due to version precedence [{} vs. {}]", this.toString(), other.toString(), 
thisVersion, thatVersion);
                 }
                 return 1;
             }
             else if(i < 0) {
                 if(LOG.isDebugEnabled()) {
-                    LOG.debug("Mapping [{}] has a lower precedence than [{}] 
due to version precedence [{} vs. {}]", this, other, thisVersion, thatVersion);
+                    LOG.debug("Mapping [{}] has a lower precedence than [{}] 
due to version precedence [{} vs. {}]", this.toString(), other.toString(), 
thisVersion, thatVersion);
                 }
                 return -1;
             }
@@ -945,35 +946,35 @@ public class RegexUrlMapping extends AbstractUrlMapping {
     private int evaluatePluginOrder(UrlMapping other) {
         if (isDefinedInPlugin() && !other.isDefinedInPlugin()) {
             if(LOG.isDebugEnabled()) {
-                LOG.debug("Mapping [{}] has lower precedence than [{}] because 
the latter has priority over plugins", this, other);
+                LOG.debug("Mapping [{}] has lower precedence than [{}] because 
the latter has priority over plugins", this.toString(), other.toString());
             }
             return -1;
         } else if (!isDefinedInPlugin() && other.isDefinedInPlugin()) {
             if(LOG.isDebugEnabled()) {
-                LOG.debug("Mapping [{}] has higher precedence than [{}] 
because it has priority over plugins", this, other);
+                LOG.debug("Mapping [{}] has higher precedence than [{}] 
because it has priority over plugins", this.toString(), other.toString());
             }
             return 1;
         } else {
             if (isDefinedInPlugin()) {
                 if (pluginIndex > other.getPluginIndex()) {
                     if(LOG.isDebugEnabled()) {
-                        LOG.debug("Mapping [{}] has higher precedence than 
[{}] because it was loaded after", this, other);
+                        LOG.debug("Mapping [{}] has higher precedence than 
[{}] because it was loaded after", this.toString(), other.toString());
                     }
                     return 1;
                 } else if (pluginIndex < other.getPluginIndex()) {
                     if(LOG.isDebugEnabled()) {
-                        LOG.debug("Mapping [{}] has lower precedence than [{}] 
because it was loaded before", this, other);
+                        LOG.debug("Mapping [{}] has lower precedence than [{}] 
because it was loaded before", this.toString(), other.toString());
                     }
                     return -1;
                 } else {
                     if(LOG.isDebugEnabled()) {
-                        LOG.debug("Mapping [{}] has equal precedence with 
mapping [{}]", this, other);
+                        LOG.debug("Mapping [{}] has equal precedence with 
mapping [{}]", this.toString(), other.toString());
                     }
                     return 0;
                 }
             } else {
                 if(LOG.isDebugEnabled()) {
-                    LOG.debug("Mapping [{}] has equal precedence with mapping 
[{}]", this, other.toString());
+                    LOG.debug("Mapping [{}] has equal precedence with mapping 
[{}]", this.toString(), other.toString());
                 }
                 return 0;
             }
@@ -1012,7 +1013,7 @@ public class RegexUrlMapping extends AbstractUrlMapping {
         String[] tokens = mapping.getUrlData().getTokens();
         int count = 0;
         for (String token : tokens) {
-            if (!isSingleWildcard(token) && !token.isEmpty()) count++;
+            if (!isSingleWildcard(token) && !"".equals(token)) count++;
         }
         return count;
     }
@@ -1030,7 +1031,7 @@ public class RegexUrlMapping extends AbstractUrlMapping {
         int count = 0;
         for (String token : tokens) {
             token = token.replace(OPTIONAL_EXTENSION_WILDCARD, 
"").replace(CAPTURED_DOUBLE_WILDCARD,"").replace(CAPTURED_WILDCARD,"");
-            if (!token.isEmpty()) count++;
+            if (!"".equals(token)) count++;
         }
         return count;
     }

Reply via email to