Why there is an "IllegalStateException: Attempt to mutate in notification" exception when using mutual binding with converter?

StackOverflow https://stackoverflow.com/questions/15163002

  •  16-03-2022
  •  | 
  •  

Question

Maybe I've missed something in understanding Griffon's converter & reverseConverter. What is wrong and how should I fix this?

Thanks to @tim_yates for the edit, but I find out that I also got the same error not only in Griffon but also in plain Groovy like this:

import groovy.beans.Bindable
import groovy.swing.SwingBuilder

class Model {
    @Bindable String name
    @Bindable Integer score
}

Model m = new Model()

new SwingBuilder().edt {
    frame(title: 'Untitled', pack: true, show: true) {
        flowLayout()
        label("Name: ")
        textField(columns: 10, text: bind("name", source: m, mutual: true))
        label("Score: ")
        textField(columns: 3, text: bind("score", source: m, 
            reverseConverter: { it.isEmpty()? 0: Integer.parseInt(it) },
            converter: { String.valueOf(it) },
            mutual: true))
        button("Save", actionPerformed: { 
            println "You're saving ${m.name} with score ${m.score}"
        })
        button("Reset Input To Default", actionPerformed: {
            edt {
              m.name = "defaultName"
              m.score = 50
            }
        })
    }
}

The stacktrace is more verbose:

Result: groovy.swing.SwingBuilder@ae20b6Exception in thread "AWT-EventQueue-0" 
java.lang.IllegalStateException: Attempt to mutate in notification
    at javax.swing.text.AbstractDocument.writeLock(AbstractDocument.java:1338)
    at javax.swing.text.AbstractDocument.replace(AbstractDocument.java:659)
    at javax.swing.text.JTextComponent.setText(JTextComponent.java:1718)
    at sun.reflect.GeneratedMethodAccessor276.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
    at groovy.lang.MetaClassImpl.setProperty(MetaClassImpl.java:2404)
    at groovy.lang.MetaClassImpl.setProperty(MetaClassImpl.java:3341)
    at org.codehaus.groovy.runtime.InvokerHelper.setProperty(InvokerHelper.java:196)
    at groovy.beans.DefaultPropertyWriter.write(DefaultPropertyWriter.java:28)
    at groovy.beans.DefaultPropertyAccessor.write(DefaultPropertyAccessor.java:30)
    at org.codehaus.groovy.binding.PropertyBinding.setBeanProperty(PropertyBinding.java:236)
    at org.codehaus.groovy.binding.PropertyBinding.access$000(PropertyBinding.java:51)
    at org.codehaus.groovy.binding.PropertyBinding$2.run(PropertyBinding.java:189)
    at org.codehaus.groovy.binding.PropertyBinding.updateTargetValue(PropertyBinding.java:196)
    at org.codehaus.groovy.binding.AbstractFullBinding.fireBinding(AbstractFullBinding.java:51)
    at org.codehaus.groovy.binding.AbstractFullBinding.update(AbstractFullBinding.java:55)
    at org.codehaus.groovy.binding.PropertyBinding$PropertyFullBinding.propertyChange(PropertyBinding.java:279)
    at java.beans.PropertyChangeSupport.fire(PropertyChangeSupport.java:335)
    at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:328)
    at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:263)
    at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:283)
    at java_beans_PropertyChangeSupport$firePropertyChange$0.call(Unknown Source)
    at Model.firePropertyChange(test.groovy)
    at Model$firePropertyChange.callCurrent(Unknown Source)
    at Model.setScore(test.groovy)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
    at groovy.lang.MetaClassImpl.setProperty(MetaClassImpl.java:2404)
    at groovy.lang.MetaClassImpl.setProperty(MetaClassImpl.java:3341)
    at Model.setProperty(test.groovy)
    at org.codehaus.groovy.runtime.InvokerHelper.setProperty(InvokerHelper.java:192)
    at groovy.beans.DefaultPropertyWriter.write(DefaultPropertyWriter.java:28)
    at groovy.beans.DefaultPropertyAccessor.write(DefaultPropertyAccessor.java:30)
    at org.codehaus.groovy.binding.PropertyBinding.setBeanProperty(PropertyBinding.java:236)
    at org.codehaus.groovy.binding.PropertyBinding.access$000(PropertyBinding.java:51)
    at org.codehaus.groovy.binding.PropertyBinding$2.run(PropertyBinding.java:189)
    at org.codehaus.groovy.binding.PropertyBinding.updateTargetValue(PropertyBinding.java:220)
    at org.codehaus.groovy.binding.AbstractFullBinding.fireBinding(AbstractFullBinding.java:51)
    at org.codehaus.groovy.binding.AbstractFullBinding.update(AbstractFullBinding.java:55)
    at groovy.swing.binding.JTextComponentTextBinding.removeUpdate(JTextComponentProperties.java:85)
    at javax.swing.text.AbstractDocument.fireRemoveUpdate(AbstractDocument.java:260)
    at javax.swing.text.AbstractDocument.handleRemove(AbstractDocument.java:623)
    at javax.swing.text.AbstractDocument.remove(AbstractDocument.java:591)
    at javax.swing.text.AbstractDocument.replace(AbstractDocument.java:667)
    at javax.swing.text.JTextComponent.setText(JTextComponent.java:1718)
    at sun.reflect.GeneratedMethodAccessor276.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
    at groovy.lang.MetaClassImpl.setProperty(MetaClassImpl.java:2404)
    at groovy.lang.MetaClassImpl.setProperty(MetaClassImpl.java:3341)
    at org.codehaus.groovy.runtime.InvokerHelper.setProperty(InvokerHelper.java:196)
    at groovy.beans.DefaultPropertyWriter.write(DefaultPropertyWriter.java:28)
    at groovy.beans.DefaultPropertyAccessor.write(DefaultPropertyAccessor.java:30)
    at org.codehaus.groovy.binding.PropertyBinding.setBeanProperty(PropertyBinding.java:236)
    at org.codehaus.groovy.binding.PropertyBinding.access$000(PropertyBinding.java:51)
    at org.codehaus.groovy.binding.PropertyBinding$2.run(PropertyBinding.java:189)
    at org.codehaus.groovy.binding.PropertyBinding.updateTargetValue(PropertyBinding.java:196)
    at org.codehaus.groovy.binding.AbstractFullBinding.fireBinding(AbstractFullBinding.java:51)
    at org.codehaus.groovy.binding.AbstractFullBinding.update(AbstractFullBinding.java:55)
    at org.codehaus.groovy.binding.PropertyBinding$PropertyFullBinding.propertyChange(PropertyBinding.java:279)
    at java.beans.PropertyChangeSupport.fire(PropertyChangeSupport.java:335)
    at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:328)
    at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:263)
    at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:283)
    at java_beans_PropertyChangeSupport$firePropertyChange$0.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
    at java_beans_PropertyChangeSupport$firePropertyChange.call(Unknown Source)
    at Model.firePropertyChange(test.groovy)
    at Model$firePropertyChange.callCurrent(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:49)
    at Model$firePropertyChange.callCurrent(Unknown Source)
    at Model.setScore(test.groovy)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
    at groovy.lang.MetaClassImpl.setProperty(MetaClassImpl.java:2404)
    at groovy.lang.MetaClassImpl.setProperty(MetaClassImpl.java:3341)
    at Model.setProperty(test.groovy)
    at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.setGroovyObjectProperty(ScriptBytecodeAdapter.java:528)
    at test$_run_closure1_closure2_closure6_closure7.doCall(test.groovy:27)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:272)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:906)
    at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:39)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
    at groovy.swing.SwingBuilder.edt(SwingBuilder.groovy:330)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:361)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:906)
    at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:66)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:49)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:133)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:141)
    at test$_run_closure1_closure2_closure6.doCall(test.groovy:25)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:272)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:906)
    at groovy.lang.Closure.call(Closure.java:412)
    at org.codehaus.groovy.runtime.ConvertedClosure.invokeCustom(ConvertedClosure.java:51)
    at org.codehaus.groovy.runtime.ConversionHandler.invoke(ConversionHandler.java:82)
    at $Proxy11.actionPerformed(Unknown Source)
    at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2018)
    at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2341)
    at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
    at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
    at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252)
    at java.awt.Component.processMouseEvent(Component.java:6505)
    at javax.swing.JComponent.processMouseEvent(JComponent.java:3321)
    at java.awt.Component.processEvent(Component.java:6270)
    at java.awt.Container.processEvent(Container.java:2229)
    at java.awt.Component.dispatchEventImpl(Component.java:4861)
    at java.awt.Container.dispatchEventImpl(Container.java:2287)
    at java.awt.Component.dispatchEvent(Component.java:4687)
    at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4832)
    at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4492)
    at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4422)
    at java.awt.Container.dispatchEventImpl(Container.java:2273)
    at java.awt.Window.dispatchEventImpl(Window.java:2719)
    at java.awt.Component.dispatchEvent(Component.java:4687)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:703)
    at java.awt.EventQueue.access$000(EventQueue.java:102)
    at java.awt.EventQueue$3.run(EventQueue.java:662)
    at java.awt.EventQueue$3.run(EventQueue.java:660)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:87)
    at java.awt.EventQueue$4.run(EventQueue.java:676)
    at java.awt.EventQueue$4.run(EventQueue.java:674)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:673)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:244)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:163)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:151)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:147)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:139)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:97)
Was it helpful?

Solution

I believe this is a bug in the binding caused by the converters, as they transform the value in such a way that the binding believes it's changing while triggering. If no converters are registered (thus enforcing the same type on both ends) then no error occurs; which makes me think it's the converter's fault.

Sounds to me a ticket should be raised at http://jira.codehaus.org/browse/groovy

OTHER TIPS

From the answers above, I also agree maybe this is a bug and should be reported.

For temporary, I will use this workaround to prevent Exception from showing:

textField(columns: 3, text: bind("score", target: m, 
    converter: { it.isEmpty()? 50: Integer.parseInt(it) },
    reverseConverter: { String.valueOf(it) },
    mutual: true))

converter will need to convert empty String to the same default value in model, in this example is 50. This will satisfy the following guard in org.codehaus.groovy.binding.PropertyBinding:

public void updateTargetValue(final Object newValue) {

  Runnable runnable = new Runnable() {
     public void run() {
         ...
         if ((sourceValue==null && newValue==null) || 
              DefaultTypeTransformation.compareEqual(sourceValue, newValue)) {
            // not a change, don't fire it
            return;
         }
     }
  }

}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top