user
08/23/2018, 2:02 PMUnsupportedOperationException
if multiple threads cascade validation at the same time for the same entity.
https://github.com/grails/grails-data-mapping/blob/ef1730a42bc515cfb96cf9175d81e344d8a08a61/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/model/types/Association.java#L225
In this case, the following error will occur:
Caused by: java.lang.UnsupportedOperationException: null
at java.util.Collections$UnmodifiableCollection.add(Collections.java:1055)
at org.grails.datastore.mapping.model.types.Association.buildCascadeOperations(Association.java:235)
at org.grails.datastore.mapping.model.types.Association.getCascadeOperations(Association.java:220)
at org.grails.datastore.mapping.model.types.Association.doesCascade(Association.java:120)
at grails.gorm.validation.PersistentEntityValidator.cascadeToAssociativeProperty(PersistentEntityValidator.groovy:134)
at grails.gorm.validation.PersistentEntityValidator.validate(PersistentEntityValidator.groovy:82)
at org.grails.orm.hibernate.AbstractHibernateGormInstanceApi.save(AbstractHibernateGormInstanceApi.groovy:124)
at org.grails.datastore.gorm.GormEntity$Trait$Helper.save(GormEntity.groovy:151)
at org.grails.datastore.gorm.GormEntity$Trait$Helper$save$1.call(Unknown Source)
Any state on the PersistentEntityValidator
needs to be thread-safe since the same validator instance is used for entities of the same type.
In this case, there are a few options:
1. Make the buildCascadeOptions
synchronized which would fix the problem (although it does still allow multiple initializations of the options, in this case it would be fine).
2. Per @jameskleeh making the line below a local variable would also fix the issue.
https://github.com/grails/grails-data-mapping/blob/ef1730a42bc515cfb96cf9175d81e344d8a08a61/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/model/types/Association.java#L231
1. Another option (not discussed in slack) is why this is even lazily initialized in the first place. Why not just call it in the constructor so the object fully constructed before it can be used. It doesn't seem like it would be that expensive unless there's some catch-22 where the mappedForm
can't be used until the Association is built or something along those lines. I assume there's some reason why the cascadeOptions
are lazily initialized to begin with, but worth throwing out there.
Environment
Grails 3.3.8
GORM 6.1.11.BUILD-SNAPSHOT
Oracle jdk1.8.0_181amanda.hinchman-dominguez
08/23/2018, 2:02 PM