codeconsole opened a new pull request, #16209:
URL: https://github.com/apache/grails-core/pull/16209

   ### Problem
   
   A domain property declared as a **raw** collection loses its data during 
binding on 8.0.0-M6. Given:
   
   ```groovy
   class TopicType {
       List statusFilters = []          // no type argument
   }
   
   new TopicType(statusFilters: [[label: 'Answered', param: 'status=resolved']])
   ```
   
   every map element is replaced by an empty `new java.lang.Object()`. With 
GORM for MongoDB the write then fails:
   
   ```
   org.bson.codecs.configuration.CodecConfigurationException:
       Can't find a codec for CodecCacheKey{clazz=class java.lang.Object, 
types=null}
     at CodecExtensions$ListCodec.encode(CodecExtensions.groovy:362)
     at BasicCollectionTypeEncoder.encode(BasicCollectionTypeEncoder.groovy:64)
     at BsonPersistentEntityCodec.encode(BsonPersistentEntityCodec.groovy:229)
   ```
   
   The exception is incidental — it is the first thing to notice that the 
elements are no longer maps. Without a codec in the path the empty objects 
would simply be persisted, so the data is lost either way. This worked on 
8.0.0-M5.
   
   ### Cause
   
   #15947 changed collection binding so that a `Map` element is instantiated as 
the component type and bound through the allowlist, rather than passed to a map 
constructor:
   
   ```diff
   - itemsWhichNeedBinding << item
   + def instance = instantiateAndBindNestedOrUseMapConstructor(referencedType, 
item, itemBindingSource, ...)
   + if (instance != null) { itemsWhichNeedBinding << instance }
   ```
   
   That is correct for a real nested type, but the component type is not always 
one. `Basic#componentType` falls back to `Object.class` when a property carries 
no generic signature, so a raw collection arrives with `referencedType == 
Object`. `Object` has a public no-arg constructor, so 
`getDeclaredConstructor().newInstance()` succeeds, and `Object` declares no 
properties, so `bindNested` has nowhere to put the map's contents.
   
   This also escapes the opt-in. The instantiation is in the `try`, while 
`isDenyByDefaultEnabled()` guards only the map-constructor fallback in the 
`catch` — so an application that never set 
`grails.databinding.legacyBindableDefault=false` still loses the data, contrary 
to #15947's stated intent that unconfigured applications keep binding 
permissively.
   
   ### Fix
   
   Treat `Object` (and `null`) as "not a nested type" and keep the element as 
it stands:
   
   ```groovy
   if (referencedType == null || referencedType == Object) {
       return value
   }
   ```
   
   Nothing can be mass-assigned through a value that is never used as a 
property source, so the hardening loses nothing: every other component type 
still instantiates and binds through the allowlist, and `bindable: false` is 
unaffected. Applied to `GrailsWebDataBinder` and to `SimpleDataBinder`, which 
is exposed to the same value because `getReferencedTypeForCollection` is 
overridden by the GORM-aware subclass and dispatches virtually from 
`SimpleDataBinder`'s own collection paths.
   
   ### Tests
   
   Adds a regression test to `GrailsWebDataBinderSpec` (with a 
`RawCollectionContainer` domain). It fails on unmodified `8.0.x` with exactly 
the production symptom:
   
   ```
   [java.lang.Object@7c79f2cf, java.lang.Object@63551c66]
   ```
   
   and passes with the fix. `:grails-databinding-core:test`, 
`:grails-web-databinding:test` (including `DenyByDefaultConfigSpec`) and the 
`grails.web.databinding.*` suite in `grails-test-suite-persistence` are all 
green.
   
   Also verified end to end against the real application that hit this: with 
these two jars built at `8.0.0-M6` and substituted into an otherwise stock M6 
app, the raw-collection domain that previously failed now boots and persists 
its maps intact.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to