Skip to content

GrailsParameterMap: params['identifier'] and params['request'] resolve to bean properties instead of map entries on Groovy 5 #16280

Description

@sbglasius

Expected Behavior

Subscript and dot access on a GrailsParameterMap always address a request parameter:

def params = new GrailsParameterMap(request)
params['identifier'] = 'id1'
assert params['identifier'] == 'id1'

getIdentifier() and getRequest() remain reachable as explicit method calls, with params.getIdentifier() returning the id entry of the map — unchanged.

Actual Behaviour

On Groovy 5 (reproduced on 5.0.6 and 5.1.0) the assignment throws:

groovy.lang.ReadOnlyPropertyException: Cannot set read-only property: identifier for class: grails.web.servlet.mvc.GrailsParameterMap
	at groovy.lang.MetaClassImpl.setProperty(MetaClassImpl.java:2837)
	at org.codehaus.groovy.runtime.InvokerHelper.setProperty(InvokerHelper.java:187)
	at org.codehaus.groovy.runtime.DefaultGroovyMethods.putAt(DefaultGroovyMethods.java:12191)

Groovy 5 changed runtime method selection for classes implementing Map: for a String key, DefaultGroovyMethods.putAt(Object, String, Object) (→ setProperty) is now preferred over putAt(Map, K, V) (→ put), and getAt(Object, String) (→ getProperty) over getAt(Map, Object). Because GrailsParameterMap declares getIdentifier() and getRequest() with no setters, those names became read-only properties rather than ordinary map keys.

Expression Groovy 4.0.30 Groovy 5.x
params['identifier'] = 'x' map put throws ReadOnlyPropertyException
params['request'] = 'x' map put throws ReadOnlyPropertyException
params['identifier'] map value getIdentifier()params.id
params['request'] map value the HttpServletRequest
params.identifier / params.request map value getter result

A request parameter named identifier or request is therefore silently unreachable, and writing one throws. GroovyPageAttributes is affected the same way through the shared base grails.util.AbstractTypeConvertingMapattrs['gspTagSyntaxCall'] = x writes the field instead of the map.

Steps To Reproduce

  1. new GrailsParameterMap(new MockHttpServletRequest())
  2. map['identifier'] = 'id1'
  3. ReadOnlyPropertyException is thrown.

Added as grails.web.servlet.mvc.GrailsParameterMapTests#testAddingIdentifierParam.

Environment Information

  • Grails: 8.0.0-SNAPSHOT (8.0.x)
  • Groovy: 5.1.0 (also reproduced on 5.0.6; works on 4.0.30)
  • JDK: 21+

Only 8.0.x is affected — 7.0.x is on Groovy 4.x.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions