You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by jd...@apache.org on 2026/10/02 22:24:15 UTC
(grails-core) branch 9.0.x updated (ac1bd298ad -> d6a3c5532e)
This is an automated email from the ASF dual-hosted git repository.
jdaugherty pushed a change to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/grails-core.git
from ac1bd298ad Merge branch '8.1.x' into 9.0.x
add b4b09c4d0d Start every context of a GrailsAppBuilder hierarchy with a GrailsApp
add 543ce0d454 Merge branch 'fix/grails-app-builder-hierarchies' into feat/spring-application-builder
add 9e8a018ccd Make GrailsAppBuilder hierarchies hold exactly one Grails application
add 21ac4528b2 Merge pull request #16456 from apache/feat/spring-application-builder
add 3c5dd9c732 Updated `getModel()` with fallback: when no `ModelAndView` is set in the request, it returns the value stored under `GrailsApplicationAttributes.TEMPLATE_MODEL`:
add 774f97ce5e Merge branch '8.0.x' into fix/12081-interceptor-model-unavailable
add f9e483ceef Document template models for interceptors and cover both test apps
add 9f05303872 Attach Hibernate 7 proxies created by proxy(id) to the current session
add c29349dd69 Assert that proxy() returns an uninitialized proxy in HibernateGormStaticApiSpec
add 813221ea4c Merge pull request #16484 from apache/fix/hibernate7-proxy-attached-to-session
add 69ea3bc09c Apply the configured nullable default to properties copied with importFrom
add 3649ae06e1 Test importFrom's nullable default through the application config
add fc2565c948 Merge pull request #16474 from matrei/fix/import-from-default-nullable
add b570363fb8 Close the constant-text gaps in the query safety check
add f12375f629 Run finally blocks before jumps land and scan anonymous inner classes
add 267360fc9f Judge locals the walk cannot follow by every assignment to them
add 84b1e8a4d7 Merge pull request #16482 from matrei/fix/gorm-query-safety-constant-text
add 094c562459 Merge pull request #16462 from apache/fix/grails-app-builder-hierarchies
add 28db5ad989 Merge branch '8.0.x' into fix/12081-interceptor-model-unavailable
add e8ff211dc8 Document the dataSource defaults that are actually applied
add c56836b2b8 Register the default dataSource bean without dataSource configuration
add 2f54bbe766 Cover the order of the data source names and clarify the guide
add d8a001d4f3 Use an explicit LinkedHashSet and document excluding GORM for Hibernate
add 2d50fa049e Merge pull request #16458 from apache/fix/16455-default-datasource-bean
add b8950af8c9 Preserve servlet Errors through interceptor completion
add 6108b9f4ff Merge pull request #16467 from apache/fix/interceptor-error-throwables
add cffecea35f Link guide cross references to the chapter page that holds their target
add d8db2d173e Link cross references on sub-section and reference pages to their chapter
add 6a5bb12c0e Merge pull request #16476 from matrei/docs/guide-cross-chapter-links-7.0.x
add c8912e33f4 Merge branch '7.0.x' into 7.1.x
add b47e4dcb7a Merge branch '7.1.x' into 7.2.x
add 43b856c92a Merge branch '7.2.x' into 8.0.x
add 54fc616aa3 Merge branch '8.0.x' into fix/16454-datasource-metadata-defaults
add fa0062a9cd Publish the dataSource configuration metadata from grails-datamapping-core
add 44be66fbb7 Merge pull request #16457 from apache/fix/16454-datasource-metadata-defaults
add 8f4287c647 Merge branch '8.0.x' into fix/12081-interceptor-model-unavailable
add ff1fc7ec98 Merge pull request #16453 from ruthst00/fix/12081-interceptor-model-unavailable
add 9f19f899fb Merge branch '8.0.x' into 8.1.x
new a86876d51f Merge branch '8.1.x' into 9.0.x
new d6a3c5532e Follow Groovy 6 switch expressions in the GORM query safety check
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../src/main/groovy/grails/doc/DocPublisher.groovy | 94 +-
.../test/groovy/grails/doc/DocPublisherSpec.groovy | 175 +++
.../src/main/groovy/grails/boot/GrailsApp.groovy | 48 +-
.../groovy/grails/boot/GrailsAppBuilder.groovy | 409 +++++-
.../config/GrailsApplicationPostProcessor.groovy | 60 +-
...GrailsEarlyPluginRegistrationPostProcessor.java | 14 +
.../additional-spring-configuration-metadata.json | 88 --
.../grails/boot/DevelopmentModeWatchSpec.groovy | 54 +
.../groovy/grails/boot/GrailsAppBuilderSpec.groovy | 530 ++++++++
.../boot/GrailsAppContextHierarchySpec.groovy | 172 +++
.../EarlyPluginRegistrationOrderingSpec.groovy | 61 +
...rnateDatastoreConnectionSourcesRegistrar.groovy | 3 +-
.../tests/ImportFromDefaultNullableSpec.groovy | 87 ++
...eDatastoreConnectionSourcesRegistrarSpec.groovy | 28 +
.../HibernateDatastoreSpringInitializer.groovy | 14 +-
.../HibernateDatastoreSpringInitializerSpec.groovy | 79 +-
.../orm/hibernate/proxy/HibernateProxyHandler.java | 9 +-
...rnateDatastoreConnectionSourcesRegistrar.groovy | 3 +-
.../tests/ImportFromDefaultNullableSpec.groovy | 87 ++
.../hibernate/HibernateGormStaticApiSpec.groovy | 8 +-
.../proxy/HibernateProxyHandler7Spec.groovy | 41 +
...eDatastoreConnectionSourcesRegistrarSpec.groovy | 27 +
.../HibernateDatastoreSpringInitializer.groovy | 14 +-
.../HibernateDatastoreSpringInitializerSpec.groovy | 70 +-
grails-datamapping-core/build.gradle | 4 +
.../transform/GormQuerySafetyTransformer.java | 1328 +++++++++++++++++---
.../additional-spring-configuration-metadata.json | 91 ++
.../DataSourceConfigurationMetadataSpec.groovy | 107 ++
.../GormQuerySafetyTransformerSpec.groovy | 360 ++++++
.../builder/ConstrainedPropertyBuilder.java | 17 +-
.../eval/DefaultConstraintEvaluator.java | 2 +-
.../grails/gorm/validation/ImportFromSpec.groovy | 91 ++
.../eval/DefaultConstraintEvaluatorSpec.groovy | 40 +
grails-datasource/build.gradle | 4 +
.../datasource/DataSourceGrailsPluginSpec.groovy | 39 +
grails-doc/build.gradle | 3 +
.../guide/backgroundJobs/backgroundJobsBasic.adoc | 2 +-
.../conf/applicationClass/applicationBuilder.adoc | 127 ++
grails-doc/src/en/guide/conf/dataSource.adoc | 13 +-
.../dataSource/automaticDatabaseMigration.adoc | 2 +-
.../conventionOverConfiguration.adoc | 4 +-
grails-doc/src/en/guide/introduction/whatsNew.adoc | 16 +
.../en/guide/security/securingAgainstAttacks.adoc | 8 +-
.../springSecurity/acl/introduction.adoc | 2 +-
.../springSecurity/acl/tutorial.adoc | 2 +-
.../springSecurity/cas/introduction.adoc | 2 +-
.../securityPlugins/springSecurity/cas/usage.adoc | 2 +-
.../springSecurity/core/authentication/ajax.adoc | 2 +-
.../springSecurity/core/miscProperties.adoc | 4 +-
.../springSecurity/core/requestMappings.adoc | 2 +-
.../springSecurity/ldap/introduction.adoc | 2 +-
.../securityPlugins/springSecurity/ldap/usage.adoc | 2 +-
.../springSecurity/oauth2/introduction.adoc | 2 +-
.../springSecurity/rest/whatsNew20.adoc | 2 +-
.../securityPlugins/springSecurity/ui/acls.adoc | 2 +-
.../springSecurity/ui/introduction.adoc | 2 +-
.../interceptors/definingInterceptors.adoc | 38 +-
grails-doc/src/en/guide/toc.yml | 1 +
.../src/en/guide/upgrading/upgrading80x.adoc | 20 +
.../en/guide/validation/sharingConstraints.adoc | 3 +
grails-doc/src/en/ref/Command Line/stop-app.adoc | 2 +-
grails-doc/src/en/ref/Constraints.adoc | 3 +
grails-doc/src/en/ref/Plug-ins/codecs.adoc | 2 +-
grails-doc/src/en/ref/Tags - Fields/field.adoc | 2 +-
.../main/groovy/grails/artefact/Interceptor.groovy | 18 +-
...ailsInterceptorHandlerInterceptorAdapter.groovy | 12 +-
...InterceptorHandlerInterceptorAdapterSpec.groovy | 104 ++
.../groovy/grails/artefact/InterceptorSpec.groovy | 48 +
.../skills/grails-8-upgrade/SKILL.md | 6 +
.../app1/grails-app/conf/spring/resources.groovy | 6 +
.../controllers/functionaltests/UrlMappings.groovy | 3 +
...terceptor.groovy => ErrorPageController.groovy} | 24 +-
...erceptor.groovy => ErrorPageInterceptor.groovy} | 24 +-
.../_simpleTemplate.gsp => errorPage/handle.gsp} | 5 +-
.../TomcatErrorPageInterceptorSpec.groovy | 76 ++
.../interceptors/StaticInitFailure.groovy | 15 +-
.../interceptors/StaticInitFailureFilter.groovy | 22 +-
.../build.gradle | 28 +-
.../grails-app/conf/application.yml | 34 +-
.../grails-app/conf/logback.xml | 0
.../grails-app/domain/hierarchycli/Visitor.groovy} | 6 +-
.../init/hierarchycli/Application.groovy | 60 +
.../services/hierarchycli/VisitorService.groovy} | 23 +-
.../hierarchycli/ReportingCommandSpec.groovy | 145 +++
.../hierarchycli/command/ReportingCommand.groovy | 36 +-
.../command/VisitorReportRunner.groovy | 71 ++
.../{app4 => context-hierarchy}/build.gradle | 14 +-
.../grails-app/conf/application.yml | 68 +-
.../grails-app/conf/logback.xml | 0
.../contexthierarchy/HierarchyController.groovy | 65 +
.../contexthierarchy}/UrlMappings.groovy | 7 +-
.../domain/contexthierarchy/Visitor.groovy} | 5 +-
.../init/contexthierarchy}/Application.groovy | 15 +-
.../contexthierarchy/VisitorService.groovy} | 23 +-
.../grails-app/views/hierarchy/greet.gsp} | 8 +-
.../contexthierarchy/AnotherApplication.groovy} | 15 +-
.../SecondGrailsApplicationRefusedSpec.groovy | 59 +
.../SharedParentGrailsChildSpec.groovy | 159 +++
.../contexthierarchy/shared/AuditTrail.groovy | 28 +-
.../shared/SharedConfiguration.groovy | 30 +-
.../grails-app/domain/gorm/Customer.groovy} | 7 +-
.../groovy/gorm/ImportFromSpec.groovy | 57 +
...{TestValidate.groovy => CustomerCommand.groovy} | 7 +-
.../functional/tests/ModelInterceptor.groovy | 2 +-
...ller.groovy => RenderTemplateController.groovy} | 15 +-
.../grails-app/views/renderTemplate/_snippet.gsp} | 4 +-
.../tests/ModelInterceptorIntSpec.groovy | 25 +
.../grails-app/conf/spring/resources.groovy | 9 +-
...ontroller.groovy => ErrorPageController.groovy} | 22 +-
.../undertowapp/ErrorPageInterceptor.groovy} | 16 +-
.../controllers/undertowapp/UrlMappings.groovy | 1 +
.../grails-app/views/errorPage/handle.gsp} | 5 +-
.../UndertowErrorPageInterceptorSpec.groovy | 81 ++
.../groovy/undertowapp/StaticInitFailure.groovy} | 14 +-
.../undertowapp/StaticInitFailureFilter.groovy | 22 +-
.../functional/tests/ModelInterceptor.groovy | 2 +-
.../tests/RenderTemplateController.groovy} | 15 +-
.../grails-app/views/renderTemplate/_snippet.gsp} | 4 +-
.../tests/ModelInterceptorIntSpec.groovy | 25 +
settings.gradle | 4 +
120 files changed, 5495 insertions(+), 636 deletions(-)
create mode 100644 build-logic/docs-core/src/test/groovy/grails/doc/DocPublisherSpec.groovy
create mode 100644 grails-core/src/test/groovy/grails/boot/GrailsAppBuilderSpec.groovy
create mode 100644 grails-core/src/test/groovy/grails/boot/GrailsAppContextHierarchySpec.groovy
create mode 100644 grails-data-hibernate5/core/src/test/groovy/grails/gorm/tests/ImportFromDefaultNullableSpec.groovy
create mode 100644 grails-data-hibernate7/core/src/test/groovy/grails/gorm/tests/ImportFromDefaultNullableSpec.groovy
create mode 100644 grails-datamapping-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json
create mode 100644 grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/jdbc/connections/DataSourceConfigurationMetadataSpec.groovy
create mode 100644 grails-doc/src/en/guide/conf/applicationClass/applicationBuilder.adoc
copy grails-test-examples/app1/grails-app/controllers/functionaltests/interceptors/{HttpMethodMatchingInterceptor.groovy => ErrorPageController.groovy} (62%)
copy grails-test-examples/app1/grails-app/controllers/functionaltests/interceptors/{HttpMethodMatchingInterceptor.groovy => ErrorPageInterceptor.groovy} (69%)
copy grails-test-examples/app1/grails-app/views/{tagLibTest/_simpleTemplate.gsp => errorPage/handle.gsp} (89%)
create mode 100644 grails-test-examples/app1/src/integration-test/groovy/functionaltests/interceptors/TomcatErrorPageInterceptorSpec.groovy
copy grails-databinding-core/src/main/groovy/org/grails/databinding/converters/StructuredSqlDateBindingEditor.groovy => grails-test-examples/app1/src/main/groovy/functionaltests/interceptors/StaticInitFailure.groovy (72%)
copy grails-spring-security/rest/core/src/main/groovy/grails/plugin/springsecurity/rest/token/bearer/BearerTokenAccessDeniedHandler.groovy => grails-test-examples/app1/src/main/groovy/functionaltests/interceptors/StaticInitFailureFilter.groovy (58%)
copy grails-test-examples/{hibernate7/grails-data-service-multi-datasource => context-hierarchy-cli}/build.gradle (64%)
copy grails-test-examples/{scaffolding-fields => context-hierarchy-cli}/grails-app/conf/application.yml (68%)
copy grails-test-examples/{app4 => context-hierarchy-cli}/grails-app/conf/logback.xml (100%)
copy grails-test-examples/{gorm/grails-app/domain/gorm/AbstractParent.groovy => context-hierarchy-cli/grails-app/domain/hierarchycli/Visitor.groovy} (93%)
create mode 100644 grails-test-examples/context-hierarchy-cli/grails-app/init/hierarchycli/Application.groovy
copy grails-test-examples/{hibernate7/app1/grails-app/controllers/functionaltests/BazController.groovy => context-hierarchy-cli/grails-app/services/hierarchycli/VisitorService.groovy} (73%)
create mode 100644 grails-test-examples/context-hierarchy-cli/src/integration-test/groovy/hierarchycli/ReportingCommandSpec.groovy
copy grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/core/SoftThreadLocalMap.java => grails-test-examples/context-hierarchy-cli/src/main/groovy/hierarchycli/command/ReportingCommand.groovy (59%)
create mode 100644 grails-test-examples/context-hierarchy-cli/src/main/groovy/hierarchycli/command/VisitorReportRunner.groovy
copy grails-test-examples/{app4 => context-hierarchy}/build.gradle (75%)
copy grails-test-examples/{spring-security/core/integration-test-app => context-hierarchy}/grails-app/conf/application.yml (70%)
copy grails-test-examples/{app4 => context-hierarchy}/grails-app/conf/logback.xml (100%)
create mode 100644 grails-test-examples/context-hierarchy/grails-app/controllers/contexthierarchy/HierarchyController.groovy
copy {end-to-end/spring-dependency-management/grails-app/controllers => grails-test-examples/context-hierarchy/grails-app/controllers/contexthierarchy}/UrlMappings.groovy (91%)
copy grails-test-examples/{app1/grails-app/domain/functionaltests/Baz.groovy => context-hierarchy/grails-app/domain/contexthierarchy/Visitor.groovy} (92%)
copy {grails-spring-security/plugin/src/main/groovy/grails/plugin/springsecurity => grails-test-examples/context-hierarchy/grails-app/init/contexthierarchy}/Application.groovy (62%)
copy grails-test-examples/{hibernate7/app1/grails-app/controllers/functionaltests/BazController.groovy => context-hierarchy/grails-app/services/contexthierarchy/VisitorService.groovy} (73%)
copy grails-test-examples/{geb-context-path/grails-app/views/greeting/index.gsp => context-hierarchy/grails-app/views/hierarchy/greet.gsp} (86%)
copy grails-test-examples/{demo33/grails-app/init/demo/Application.groovy => context-hierarchy/src/integration-test/groovy/contexthierarchy/AnotherApplication.groovy} (83%)
create mode 100644 grails-test-examples/context-hierarchy/src/integration-test/groovy/contexthierarchy/SecondGrailsApplicationRefusedSpec.groovy
create mode 100644 grails-test-examples/context-hierarchy/src/integration-test/groovy/contexthierarchy/SharedParentGrailsChildSpec.groovy
copy grails-databinding-core/src/main/groovy/org/grails/databinding/ClosureValueConverter.groovy => grails-test-examples/context-hierarchy/src/main/groovy/contexthierarchy/shared/AuditTrail.groovy (65%)
mode change 100755 => 100644
copy grails-controllers/src/main/groovy/grails/compiler/traits/ControllerTraitInjector.groovy => grails-test-examples/context-hierarchy/src/main/groovy/contexthierarchy/shared/SharedConfiguration.groovy (65%)
copy grails-test-examples/{hibernate7/gorm/grails-app/domain/gorm/City.groovy => gorm/grails-app/domain/gorm/Customer.groovy} (88%)
create mode 100644 grails-test-examples/gorm/src/integration-test/groovy/gorm/ImportFromSpec.groovy
copy grails-test-examples/gorm/src/main/groovy/gorm/{TestValidate.groovy => CustomerCommand.groovy} (89%)
copy grails-test-examples/hibernate7/views-functional-tests/grails-app/controllers/functional/tests/{PersonInheritanceController.groovy => RenderTemplateController.groovy} (70%)
copy grails-test-examples/{app1/grails-app/views/tags/_hello.gsp => hibernate7/views-functional-tests/grails-app/views/renderTemplate/_snippet.gsp} (92%)
copy grails-test-examples/undertow/grails-app/controllers/undertowapp/{SessionController.groovy => ErrorPageController.groovy} (60%)
copy grails-test-examples/{hibernate7/app1/grails-app/controllers/functionaltests/ErrorsControllerInterceptor.groovy => undertow/grails-app/controllers/undertowapp/ErrorPageInterceptor.groovy} (70%)
copy grails-test-examples/{app1/grails-app/views/tagLibTest/_simpleTemplate.gsp => undertow/grails-app/views/errorPage/handle.gsp} (89%)
create mode 100644 grails-test-examples/undertow/src/integration-test/groovy/undertowapp/UndertowErrorPageInterceptorSpec.groovy
copy grails-test-examples/undertow/{grails-app/init/undertowapp/Application.groovy => src/main/groovy/undertowapp/StaticInitFailure.groovy} (74%)
copy grails-spring-security/rest/core/src/main/groovy/grails/plugin/springsecurity/rest/token/bearer/BearerTokenAccessDeniedHandler.groovy => grails-test-examples/undertow/src/main/groovy/undertowapp/StaticInitFailureFilter.groovy (58%)
copy grails-test-examples/{hibernate7/views-functional-tests/grails-app/controllers/functional/tests/PersonInheritanceController.groovy => views-functional-tests/grails-app/controllers/functional/tests/RenderTemplateController.groovy} (70%)
copy grails-test-examples/{app1/grails-app/views/tags/_hello.gsp => views-functional-tests/grails-app/views/renderTemplate/_snippet.gsp} (92%)
(grails-core) 02/02: Follow Groovy 6 switch expressions in the GORM query safety check
Posted by jd...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jdaugherty pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit d6a3c5532e4e8bbc05cc24e00aafbe16918587e1
Author: James Daugherty <jd...@jdresources.net>
AuthorDate: Fri Oct 2 18:24:03 2026 -0400
Follow Groovy 6 switch expressions in the GORM query safety check
Groovy 5 compiles a switch expression to a closure called on the spot,
which the check already follows. Groovy 6 has a SwitchExpression node
whose arms end with a yield, so the check neither followed the value of
a switch expression nor tracked the locals its arms declare, and the
switch expression cases of GormQuerySafetyTransformerSpec failed.
A switch expression is now followed through the values it yields, its
arms are walked like the cases of a switch statement with each yield
leaving the expression, and the locals its arms declare or assign are
judged as a closure's are, as on Groovy 5.
---
.../transform/GormQuerySafetyTransformer.java | 191 +++++++++++++++++----
.../GormQuerySafetyTransformerSpec.groovy | 7 +
2 files changed, 168 insertions(+), 30 deletions(-)
diff --git a/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/query/transform/GormQuerySafetyTransformer.java b/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/query/transform/GormQuerySafetyTransformer.java
index fe122cdc90..0ceb4c767d 100644
--- a/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/query/transform/GormQuerySafetyTransformer.java
+++ b/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/query/transform/GormQuerySafetyTransformer.java
@@ -27,6 +27,7 @@ import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -60,6 +61,7 @@ import org.codehaus.groovy.ast.expr.ListExpression;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.ast.expr.PropertyExpression;
import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
+import org.codehaus.groovy.ast.expr.SwitchExpression;
import org.codehaus.groovy.ast.expr.TernaryExpression;
import org.codehaus.groovy.ast.expr.TupleExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
@@ -79,6 +81,7 @@ import org.codehaus.groovy.ast.stmt.SynchronizedStatement;
import org.codehaus.groovy.ast.stmt.ThrowStatement;
import org.codehaus.groovy.ast.stmt.TryCatchStatement;
import org.codehaus.groovy.ast.stmt.WhileStatement;
+import org.codehaus.groovy.ast.stmt.YieldStatement;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.control.messages.WarningMessage;
import org.codehaus.groovy.syntax.Token;
@@ -151,6 +154,8 @@ import org.grails.datastore.mapping.reflect.AstUtils;
* run any number of times, so a body that assigns a local declared outside it is re-walked from
* the merged loop-head state until that state is stable before findings are reported: an
* assignment late in the body is seen by a use earlier in it, which the next iteration reaches.
+ * The arms of a {@code switch} expression are walked like the cases of a {@code switch}
+ * statement, each {@code yield} carrying its state past the expression.
* {@code break}, {@code continue} and, in a closure, {@code return} carry their state to the
* exit or head they jump to, and a path ending in such a jump or in {@code throw} contributes
* nothing to the state after the statement. A jump or an exception that leaves a {@code try}
@@ -323,9 +328,12 @@ public class GormQuerySafetyTransformer extends ClassCodeVisitorSupport {
private List<CodeFacts> enclosingFacts = Collections.emptyList();
/** The closures being walked, innermost first. */
private final Deque<ClosureExpression> closures = new ArrayDeque<>();
+ /** The {@code switch} expressions being walked, innermost first. */
+ private final Deque<SwitchExpression> switchExpressions = new ArrayDeque<>();
/**
- * The loops, {@code switch} statements and closure bodies being walked, innermost first, that
- * a {@code break}, {@code continue} or {@code return} can carry its state to.
+ * The loops, {@code switch} statements and expressions and closure bodies being walked,
+ * innermost first, that a {@code break}, {@code continue}, {@code yield} or {@code return}
+ * can carry its state to.
*/
private final Deque<JumpTarget> jumpTargets = new ArrayDeque<>();
/**
@@ -445,6 +453,7 @@ public class GormQuerySafetyTransformer extends ClassCodeVisitorSupport {
concatenatedStringVars.clear();
constantTextVars.clear();
closures.clear();
+ switchExpressions.clear();
facts = new CodeFacts();
}
@@ -674,6 +683,36 @@ public class GormQuerySafetyTransformer extends ClassCodeVisitorSupport {
}
}
+ /**
+ * Visits a {@code switch} expression like a {@code switch} statement (see {@link #visitSwitch}).
+ * Every arm ends with a {@code yield}, which leaves the expression the way a {@code break}
+ * leaves the statement, so the state afterwards is merged from every {@code yield}.
+ */
+ @Override
+ public void visitSwitchExpression(SwitchExpression expression) {
+ expression.getExpression().visit(this);
+ TrackingSnapshot before = snapshot();
+ JumpTarget target = new JumpTarget(null, JumpTarget.Kind.SWITCH_EXPRESSION);
+ jumpTargets.push(target);
+ switchExpressions.push(expression);
+ try {
+ TrackingSnapshot fallThrough = null;
+ for (CaseStatement caseStatement : expression.getCaseStatements()) {
+ restore(merge(before, fallThrough));
+ visitStatement(caseStatement);
+ caseStatement.getExpression().visit(this);
+ caseStatement.getCode().visit(this);
+ fallThrough = normalExit(caseStatement.getCode(), snapshot());
+ }
+ Statement defaultStatement = expression.getDefaultStatement();
+ TrackingSnapshot end = walkFrom(merge(before, fallThrough), defaultStatement);
+ restore(merge(normalExit(defaultStatement, end), target.exit), end);
+ } finally {
+ switchExpressions.pop();
+ jumpTargets.pop();
+ }
+ }
+
/**
* Visits a {@code try} statement. An exception can leave the {@code try} block after any
* statement, so each {@code catch} block starts from the merge of every state the block
@@ -902,6 +941,21 @@ public class GormQuerySafetyTransformer extends ClassCodeVisitorSupport {
}
}
+ @Override
+ public void visitYieldStatement(YieldStatement statement) {
+ super.visitYieldStatement(statement);
+ // A yield leaves the innermost switch expression; it cannot cross a closure boundary.
+ for (JumpTarget target : jumpTargets) {
+ if (target.kind == JumpTarget.Kind.CLOSURE) {
+ return;
+ }
+ if (target.kind == JumpTarget.Kind.SWITCH_EXPRESSION) {
+ jump(target, false, snapshot());
+ return;
+ }
+ }
+ }
+
@Override
public void visitReturnStatement(ReturnStatement statement) {
super.visitReturnStatement(statement);
@@ -946,11 +1000,11 @@ public class GormQuerySafetyTransformer extends ClassCodeVisitorSupport {
/**
* The innermost enclosing loop (or, for a {@code break}, {@code switch}) that a jump leaves,
- * by label when it has one. A jump cannot cross a closure boundary.
+ * by label when it has one. A jump cannot cross a closure or {@code switch} expression boundary.
*/
private JumpTarget jumpTarget(String label, boolean loopsOnly) {
for (JumpTarget target : jumpTargets) {
- if (target.kind == JumpTarget.Kind.CLOSURE) {
+ if (target.kind == JumpTarget.Kind.CLOSURE || target.kind == JumpTarget.Kind.SWITCH_EXPRESSION) {
return null;
}
boolean kindMatches = target.kind == JumpTarget.Kind.LOOP || !loopsOnly;
@@ -977,7 +1031,8 @@ public class GormQuerySafetyTransformer extends ClassCodeVisitorSupport {
private static boolean completesAbruptly(Statement statement) {
if (statement instanceof BreakStatement || statement instanceof ContinueStatement ||
- statement instanceof ReturnStatement || statement instanceof ThrowStatement) {
+ statement instanceof ReturnStatement || statement instanceof ThrowStatement ||
+ statement instanceof YieldStatement) {
return true;
}
if (statement instanceof BlockStatement) {
@@ -1088,14 +1143,15 @@ public class GormQuerySafetyTransformer extends ClassCodeVisitorSupport {
}
/**
- * A loop, {@code switch} or closure body being walked, with the states that jumps inside it
- * carry to its exit ({@code break}) and to its head ({@code continue}, or {@code return} from
- * a closure), and the state at the end of its most recent walk.
+ * A loop, {@code switch} statement or expression, or closure body being walked, with the
+ * states that jumps inside it carry to its exit ({@code break}, or {@code yield} from a
+ * {@code switch} expression) and to its head ({@code continue}, or {@code return} from a
+ * closure), and the state at the end of its most recent walk.
*/
private static final class JumpTarget {
enum Kind {
- LOOP, SWITCH, CLOSURE
+ LOOP, SWITCH, SWITCH_EXPRESSION, CLOSURE
}
final Kind kind;
@@ -1194,6 +1250,8 @@ public class GormQuerySafetyTransformer extends ClassCodeVisitorSupport {
final Map<ClosureExpression, Set<Object>> closureLocals = new IdentityHashMap<>();
/** The innermost closure declaring each local declared inside one. */
final Map<Object, ClosureExpression> declaringClosures = new HashMap<>();
+ /** The innermost {@code switch} expression declaring each local declared inside one. */
+ final Map<Object, SwitchExpression> declaringSwitchExpressions = new HashMap<>();
/**
* The locals that hold only constant text however their assignments are ordered, filled
* in by {@link #settleConstants} once the scan is complete.
@@ -1210,7 +1268,10 @@ public class GormQuerySafetyTransformer extends ClassCodeVisitorSupport {
private final Set<Expression> statementExpressions = Collections.newSetFromMap(new IdentityHashMap<>());
private final Deque<Set<Object>> closureScopes = new ArrayDeque<>();
- /** The closure or anonymous inner class creation owning each of {@link #closureScopes}. */
+ /**
+ * The closure, anonymous inner class creation or {@code switch} expression owning each of
+ * {@link #closureScopes}.
+ */
private final Deque<Expression> scopeOwners = new ArrayDeque<>();
/** The loop and closure bodies being scanned, innermost first. */
private final Deque<BodyScope> openBodies = new ArrayDeque<>();
@@ -1237,6 +1298,20 @@ public class GormQuerySafetyTransformer extends ClassCodeVisitorSupport {
}
}
+ /**
+ * The value of a {@code switch} expression is classified where it is assigned, before its
+ * arms are walked, so the locals its arms declare or assign are judged as a closure's are.
+ */
+ @Override
+ public void visitSwitchExpression(SwitchExpression expression) {
+ openScope(expression, new HashSet<>());
+ try {
+ super.visitSwitchExpression(expression);
+ } finally {
+ closeScope();
+ }
+ }
+
private void openScope(Expression owner, Set<Object> locals) {
scopeOwners.push(owner);
closureScopes.push(locals);
@@ -1381,12 +1456,20 @@ public class GormQuerySafetyTransformer extends ClassCodeVisitorSupport {
private void declareLocal(Object key) {
owned.add(key);
- Set<Object> closureScope = closureScopes.peek();
- if (closureScope != null) {
+ // A switch expression runs in place, so its locals are also those of the scope around it.
+ Iterator<Expression> owners = scopeOwners.iterator();
+ for (Set<Object> closureScope : closureScopes) {
closureScope.add(key);
+ if (!(owners.next() instanceof SwitchExpression)) {
+ break;
+ }
+ }
+ Expression owner = scopeOwners.peek();
+ if (owner instanceof ClosureExpression) {
+ declaringClosures.put(key, (ClosureExpression) owner);
}
- if (scopeOwners.peek() instanceof ClosureExpression) {
- declaringClosures.put(key, (ClosureExpression) scopeOwners.peek());
+ else if (owner instanceof SwitchExpression) {
+ declaringSwitchExpressions.put(key, (SwitchExpression) owner);
}
for (BodyScope scope : openBodies) {
scope.locals.add(key);
@@ -1484,10 +1567,10 @@ public class GormQuerySafetyTransformer extends ClassCodeVisitorSupport {
TernaryExpression ternary = (TernaryExpression) expression;
return classify(ternary.getTrueExpression(), declaredType).or(classify(ternary.getFalseExpression(), declaredType));
}
- ClosureExpression called = calledClosure(expression);
- if (called != null) {
+ List<Expression> results = resultsOf(expression);
+ if (results != null) {
Origin origin = Origin.CONSTANT;
- for (Expression result : resultsOf(called)) {
+ for (Expression result : results) {
origin = origin.or(classify(result, declaredType));
}
return origin;
@@ -1548,9 +1631,9 @@ public class GormQuerySafetyTransformer extends ClassCodeVisitorSupport {
TernaryExpression ternary = (TernaryExpression) expression;
return isUnsafeSource(ternary.getTrueExpression()) || isUnsafeSource(ternary.getFalseExpression());
}
- ClosureExpression called = calledClosure(expression);
- if (called != null) {
- for (Expression result : resultsOf(called)) {
+ List<Expression> results = resultsOf(expression);
+ if (results != null) {
+ for (Expression result : results) {
if (isUnsafeSource(result)) {
return true;
}
@@ -1559,10 +1642,25 @@ public class GormQuerySafetyTransformer extends ClassCodeVisitorSupport {
return false;
}
+ /**
+ * The expressions whose value {@code expression} takes when it is a closure literal called on
+ * the spot, as in {@code { ... }()}, or a {@code switch} expression, or {@code null} for any
+ * other expression.
+ */
+ private static List<Expression> resultsOf(Expression expression) {
+ ClosureExpression called = calledClosure(expression);
+ if (called != null) {
+ return resultsOf(called);
+ }
+ if (expression instanceof SwitchExpression) {
+ return resultsOf((SwitchExpression) expression);
+ }
+ return null;
+ }
+
/**
* The closure literal {@code expression} calls on the spot, as in {@code { ... }()}, or
- * {@code null}. A {@code switch} expression compiles to such a call, so this is how the value
- * of one is followed.
+ * {@code null}.
*/
private static ClosureExpression calledClosure(Expression expression) {
if (expression instanceof MethodCallExpression) {
@@ -1596,6 +1694,35 @@ public class GormQuerySafetyTransformer extends ClassCodeVisitorSupport {
return results;
}
+ /**
+ * The expressions a {@code switch} expression can take: those of its own {@code yield}
+ * statements, not those of {@code switch} expressions nested in it.
+ */
+ private static List<Expression> resultsOf(SwitchExpression switchExpression) {
+ List<Expression> results = new ArrayList<>();
+ CodeVisitorSupport yields = new CodeVisitorSupport() {
+ @Override
+ public void visitYieldStatement(YieldStatement statement) {
+ results.add(statement.getExpression());
+ }
+
+ @Override
+ public void visitSwitchExpression(SwitchExpression nested) {
+ // A nested switch expression's yields give that expression its value, not this one.
+ }
+
+ @Override
+ public void visitClosureExpression(ClosureExpression nested) {
+ // A yield cannot leave a closure.
+ }
+ };
+ for (CaseStatement caseStatement : switchExpression.getCaseStatements()) {
+ caseStatement.getCode().visit(yields);
+ }
+ switchExpression.getDefaultStatement().visit(yields);
+ return results;
+ }
+
private static void addTailResults(Statement statement, List<Expression> results) {
if (statement instanceof ExpressionStatement) {
results.add(((ExpressionStatement) statement).getExpression());
@@ -1673,9 +1800,9 @@ public class GormQuerySafetyTransformer extends ClassCodeVisitorSupport {
TernaryExpression ternary = (TernaryExpression) expression;
return isConstantText(ternary.getTrueExpression()) && isConstantText(ternary.getFalseExpression());
}
- ClosureExpression called = calledClosure(expression);
- if (called != null) {
- for (Expression result : resultsOf(called)) {
+ List<Expression> results = resultsOf(expression);
+ if (results != null) {
+ for (Expression result : results) {
if (!isConstantText(result)) {
return false;
}
@@ -1723,13 +1850,17 @@ public class GormQuerySafetyTransformer extends ClassCodeVisitorSupport {
* Whether the walk cannot know what the local under {@code key} holds at this point, so it is
* judged by every assignment to it in the code: one assigned where the walk cannot place the
* assignment (see {@link CodeFacts#unordered}), one declared outside the closure being walked,
- * which may run at any later point, and one declared inside a closure that is not being walked,
- * whose state is only tracked while it is.
+ * which may run at any later point, and one declared inside a closure or {@code switch}
+ * expression that is not being walked, whose state is only tracked while it is.
*/
private boolean isJudgedByEveryAssignment(Object key) {
if (facts.unordered.contains(key) || isSharedWithEnclosingClosure(key)) {
return true;
}
+ SwitchExpression declaringSwitch = facts.declaringSwitchExpressions.get(key);
+ if (declaringSwitch != null && !switchExpressions.contains(declaringSwitch)) {
+ return true;
+ }
ClosureExpression declaring = facts.declaringClosures.get(key);
return declaring != null && !closures.contains(declaring);
}
@@ -2004,10 +2135,10 @@ public class GormQuerySafetyTransformer extends ClassCodeVisitorSupport {
TernaryExpression ternary = (TernaryExpression) argument;
return findingFor(ternary.getTrueExpression()).or(findingFor(ternary.getFalseExpression()));
}
- ClosureExpression called = calledClosure(argument);
- if (called != null) {
+ List<Expression> results = resultsOf(argument);
+ if (results != null) {
Finding finding = Finding.NONE;
- for (Expression result : resultsOf(called)) {
+ for (Expression result : results) {
finding = finding.or(findingFor(result));
}
return finding;
diff --git a/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/query/transform/GormQuerySafetyTransformerSpec.groovy b/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/query/transform/GormQuerySafetyTransformerSpec.groovy
index b0f846a9f9..7640d63110 100644
--- a/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/query/transform/GormQuerySafetyTransformerSpec.groovy
+++ b/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/query/transform/GormQuerySafetyTransformerSpec.groovy
@@ -508,6 +508,9 @@ class Book {
'constant text chosen by a switch expression' | 'String restriction = switch (title) {\n case "" -> ""\n default -> " and b.title = :title"\n }\n String q = "from Book b where 1 = 1 ${restriction}"'
'constant text yielded by a switch expression block' | 'String restriction = switch (title) {\n case "" -> { yield "" }\n default -> { yield " and b.title = :title" }\n }\n String q = "from Book b where 1 = 1 ${restriction}"'
'constant text in a local a switch expression block yields' | 'String restriction = switch (title) {\n case "" -> ""\n default -> {\n String fragment = " and b.title = :title"\n yield fragment\n }\n }\n String q = "from Book b where 1 = 1 ${restriction}"'
+ 'constant text yielded by a colon-form switch expression' | 'String restriction = switch (title) {\n case "": yield ""\n default: yield " and b.title = :title"\n }\n String q = "from Book b where 1 = 1 ${restriction}"'
+ 'constant text yielded by a nested switch expression' | 'String restriction = switch (title) {\n case "" -> ""\n default -> switch (condition) {\n case true -> " and b.title = :title"\n default -> ""\n }\n }\n String q = "from Book b where 1 = 1 ${restriction}"'
+ 'a local reassigned safely inside a switch expression arm' | 'String chosen = switch (title) {\n case "a" -> {\n String inner = "from Book b where b.title = ${title}"\n inner = "from Book b where b.title = :title"\n Book.executeQuery(inner, queryParams)\n yield inner\n }\n default -> "from Book b"\n }\n String q = "from Book b"'
'constant text returned by a closure called on the spot' | 'String restriction = {\n if (condition) {\n " and b.title = :title"\n } else {\n ""\n }\n }()\n String q = "from Book b where 1 = 1 ${restriction}"'
'constant text assigned by a multiple-assignment declaration' | 'def (String restriction, String order) = [" and b.title = :title", " order by b.title"]\n String q = "from Book b where 1 = 1 ${restriction}${order}"'
'a GString passed directly from inside a closure' | 'def g = "from Book b where b.title = ${title}"\n Book.withTransaction {\n executeQuery(g)\n }\n String q = "from Book b"'
@@ -632,6 +635,10 @@ class Book {
'in a ternary concatenated onto the query' | 'String chosen = "from Book" + (condition ? " where title = ${title}" : "")\n executeQuery(chosen)'
'in an arm of a switch expression' | 'String chosen = switch (title) {\n case "a" -> "from Book where title = ${title}"\n default -> "from Book"\n }\n executeQuery(chosen)'
'in a local a switch expression block yields' | 'String chosen = switch (title) {\n case "a" -> {\n String flattened = "from Book where title = ${title}"\n yield flattened\n }\n default -> "from Book"\n }\n executeQuery(chosen)'
+ 'in a colon-form switch expression' | 'String chosen = switch (title) {\n case "a": yield "from Book where title = ${title}"\n default: yield "from Book"\n }\n executeQuery(chosen)'
+ 'in a nested switch expression' | 'String chosen = switch (title) {\n case "a" -> switch (condition) {\n case true -> "from Book where title = ${title}"\n default -> "from Book"\n }\n default -> "from Book"\n }\n executeQuery(chosen)'
+ 'in a switch expression arm that assigns it' | 'String chosen = switch (title) {\n case "a" -> {\n q = "from Book where title = ${title}"\n yield "a"\n }\n default -> "b"\n }\n executeQuery(q)'
+ 'in a switch expression arm that executes it' | 'String chosen = switch (title) {\n case "a" -> {\n String inner = "from Book where title = ${title}"\n executeQuery(inner)\n yield "a"\n }\n default -> "b"\n }'
'in a result of a closure called on the spot' | 'String chosen = {\n if (condition) {\n "from Book where title = ${title}"\n } else {\n "from Book"\n }\n }()\n executeQuery(chosen)'
'in a ternary branch that assigns it' | 'condition ? (q = "from Book where title = ${title}") : (q = "from Book")\n executeQuery(q)'
'by a multiple assignment' | 'def other\n (q, other) = ["from Book where title = ${title}", 1]\n executeQuery(q)'
(grails-core) 01/02: Merge branch '8.1.x' into 9.0.x
Posted by jd...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jdaugherty pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit a86876d51f3446425e21e2f52a69ba1ff6e2edaa
Merge: ac1bd298ad 9f19f899fb
Author: James Daugherty <jd...@jdresources.net>
AuthorDate: Fri Oct 2 18:07:32 2026 -0400
Merge branch '8.1.x' into 9.0.x
.../src/main/groovy/grails/doc/DocPublisher.groovy | 94 +-
.../test/groovy/grails/doc/DocPublisherSpec.groovy | 175 +++
.../src/main/groovy/grails/boot/GrailsApp.groovy | 48 +-
.../groovy/grails/boot/GrailsAppBuilder.groovy | 409 ++++++-
.../config/GrailsApplicationPostProcessor.groovy | 60 +-
...GrailsEarlyPluginRegistrationPostProcessor.java | 14 +
.../additional-spring-configuration-metadata.json | 88 --
.../grails/boot/DevelopmentModeWatchSpec.groovy | 54 +
.../groovy/grails/boot/GrailsAppBuilderSpec.groovy | 530 +++++++++
.../boot/GrailsAppContextHierarchySpec.groovy | 172 +++
.../EarlyPluginRegistrationOrderingSpec.groovy | 61 +
...rnateDatastoreConnectionSourcesRegistrar.groovy | 3 +-
.../tests/ImportFromDefaultNullableSpec.groovy | 87 ++
...eDatastoreConnectionSourcesRegistrarSpec.groovy | 28 +
.../HibernateDatastoreSpringInitializer.groovy | 14 +-
.../HibernateDatastoreSpringInitializerSpec.groovy | 79 +-
.../orm/hibernate/proxy/HibernateProxyHandler.java | 9 +-
...rnateDatastoreConnectionSourcesRegistrar.groovy | 3 +-
.../tests/ImportFromDefaultNullableSpec.groovy | 87 ++
.../hibernate/HibernateGormStaticApiSpec.groovy | 8 +-
.../proxy/HibernateProxyHandler7Spec.groovy | 41 +
...eDatastoreConnectionSourcesRegistrarSpec.groovy | 27 +
.../HibernateDatastoreSpringInitializer.groovy | 14 +-
.../HibernateDatastoreSpringInitializerSpec.groovy | 70 +-
grails-datamapping-core/build.gradle | 4 +
.../transform/GormQuerySafetyTransformer.java | 1179 +++++++++++++++++---
.../additional-spring-configuration-metadata.json | 91 ++
.../DataSourceConfigurationMetadataSpec.groovy | 107 ++
.../GormQuerySafetyTransformerSpec.groovy | 353 ++++++
.../builder/ConstrainedPropertyBuilder.java | 17 +-
.../eval/DefaultConstraintEvaluator.java | 2 +-
.../grails/gorm/validation/ImportFromSpec.groovy | 91 ++
.../eval/DefaultConstraintEvaluatorSpec.groovy | 40 +
grails-datasource/build.gradle | 4 +
.../datasource/DataSourceGrailsPluginSpec.groovy | 39 +
grails-doc/build.gradle | 3 +
.../guide/backgroundJobs/backgroundJobsBasic.adoc | 2 +-
.../conf/applicationClass/applicationBuilder.adoc | 127 +++
grails-doc/src/en/guide/conf/dataSource.adoc | 13 +-
.../dataSource/automaticDatabaseMigration.adoc | 2 +-
.../conventionOverConfiguration.adoc | 4 +-
grails-doc/src/en/guide/introduction/whatsNew.adoc | 16 +
.../en/guide/security/securingAgainstAttacks.adoc | 8 +-
.../springSecurity/acl/introduction.adoc | 2 +-
.../springSecurity/acl/tutorial.adoc | 2 +-
.../springSecurity/cas/introduction.adoc | 2 +-
.../securityPlugins/springSecurity/cas/usage.adoc | 2 +-
.../springSecurity/core/authentication/ajax.adoc | 2 +-
.../springSecurity/core/miscProperties.adoc | 4 +-
.../springSecurity/core/requestMappings.adoc | 2 +-
.../springSecurity/ldap/introduction.adoc | 2 +-
.../securityPlugins/springSecurity/ldap/usage.adoc | 2 +-
.../springSecurity/oauth2/introduction.adoc | 2 +-
.../springSecurity/rest/whatsNew20.adoc | 2 +-
.../securityPlugins/springSecurity/ui/acls.adoc | 2 +-
.../springSecurity/ui/introduction.adoc | 2 +-
.../interceptors/definingInterceptors.adoc | 38 +-
grails-doc/src/en/guide/toc.yml | 1 +
.../src/en/guide/upgrading/upgrading80x.adoc | 20 +
.../en/guide/validation/sharingConstraints.adoc | 3 +
grails-doc/src/en/ref/Command Line/stop-app.adoc | 2 +-
grails-doc/src/en/ref/Constraints.adoc | 3 +
grails-doc/src/en/ref/Plug-ins/codecs.adoc | 2 +-
grails-doc/src/en/ref/Tags - Fields/field.adoc | 2 +-
.../main/groovy/grails/artefact/Interceptor.groovy | 18 +-
...ailsInterceptorHandlerInterceptorAdapter.groovy | 12 +-
...InterceptorHandlerInterceptorAdapterSpec.groovy | 104 ++
.../groovy/grails/artefact/InterceptorSpec.groovy | 48 +
.../skills/grails-8-upgrade/SKILL.md | 6 +
.../app1/grails-app/conf/spring/resources.groovy | 6 +
.../controllers/functionaltests/UrlMappings.groovy | 3 +
.../interceptors/ErrorPageController.groovy} | 24 +-
.../interceptors/ErrorPageInterceptor.groovy} | 22 +-
.../app1/grails-app/views/errorPage/handle.gsp | 18 +
.../TomcatErrorPageInterceptorSpec.groovy | 76 ++
.../interceptors/StaticInitFailure.groovy} | 25 +-
.../interceptors/StaticInitFailureFilter.groovy | 26 +-
.../context-hierarchy-cli/build.gradle | 52 +
.../grails-app/conf/application.yml | 55 +
.../grails-app/conf/logback.xml | 38 +
.../grails-app/domain/hierarchycli/Visitor.groovy} | 11 +-
.../init/hierarchycli/Application.groovy | 60 +
.../services/hierarchycli/VisitorService.groovy} | 22 +-
.../hierarchycli/ReportingCommandSpec.groovy | 145 +++
.../hierarchycli/command/ReportingCommand.groovy | 27 +-
.../command/VisitorReportRunner.groovy | 71 ++
.../context-hierarchy/build.gradle | 50 +
.../grails-app/conf/application.yml | 94 ++
.../context-hierarchy/grails-app/conf/logback.xml | 38 +
.../contexthierarchy/HierarchyController.groovy | 65 ++
.../contexthierarchy}/UrlMappings.groovy | 9 +-
.../domain/contexthierarchy/Visitor.groovy} | 11 +-
.../init/contexthierarchy/Application.groovy | 27 +-
.../contexthierarchy/VisitorService.groovy} | 22 +-
.../grails-app/views/hierarchy/greet.gsp | 32 +
.../contexthierarchy/AnotherApplication.groovy} | 23 +-
.../SecondGrailsApplicationRefusedSpec.groovy | 59 +
.../SharedParentGrailsChildSpec.groovy | 159 +++
.../contexthierarchy/shared/AuditTrail.groovy} | 26 +-
.../shared/SharedConfiguration.groovy} | 27 +-
.../grails-app/domain/gorm/Customer.groovy} | 14 +-
.../groovy/gorm/ImportFromSpec.groovy | 57 +
.../src/main/groovy/gorm/CustomerCommand.groovy} | 15 +-
.../functional/tests/ModelInterceptor.groovy | 2 +-
...ptor.groovy => RenderTemplateController.groovy} | 21 +-
.../grails-app/views/renderTemplate/_snippet.gsp | 19 +
.../tests/ModelInterceptorIntSpec.groovy | 25 +
.../grails-app/conf/spring/resources.groovy | 9 +-
...lMappings.groovy => ErrorPageController.groovy} | 23 +-
...Mappings.groovy => ErrorPageInterceptor.groovy} | 23 +-
.../controllers/undertowapp/UrlMappings.groovy | 1 +
.../undertow/grails-app/views/errorPage/handle.gsp | 18 +
.../UndertowErrorPageInterceptorSpec.groovy | 81 ++
.../groovy/undertowapp/StaticInitFailure.groovy} | 20 +-
.../undertowapp/StaticInitFailureFilter.groovy | 26 +-
.../functional/tests/ModelInterceptor.groovy | 2 +-
.../tests/RenderTemplateController.groovy} | 21 +-
.../grails-app/views/renderTemplate/_snippet.gsp | 19 +
.../tests/ModelInterceptorIntSpec.groovy | 25 +
settings.gradle | 4 +
120 files changed, 5744 insertions(+), 568 deletions(-)