Error when saving Bounds2DComponent (serialization error)

Hi Guys,

I have implemented Bounds2DComponent interface for my vision component (extending AbstractVisionComponent) and it seems to work/behave well until I trying to save project with it in Designer.

I believe I am missing some property that perhaps should be specified in according BeanInfo class. I did tried to add boundingRect as well as originalBounds properties there but I still getting serialization error.

The only hint I have is what says in javadoc of FPMILayout:

public static java.awt.geom.Rectangle2D getPreferredBounds​(javax.swing.JComponent comp)

Provides a consistent way to get the preferred bounds of a component. This is the bounding box of a component as it was last time it's parent container's layout was validated in the designer. Layouts may use this box to derive a new bounding box if the parent container changes. This function detects for the two ways that this box is stored:

Normal JComponent: Uses Jcomponent's preferred size property as the Dimension, and the FPMI_LC's preferred location Point to create the rectangle.
Bounds2DComponent: Uses the originalBounds property.

But I still unable to get it working. Besides implementing methods of Bounds2DComponent in my class, what else do I need to supplement and how? Please see error below (I did made sure that non of getBoundingRect() or getOriginalBounds() methods will ever return null, but I still getting:

Caused by: java.lang.IllegalArgumentException: null
	at com.inductiveautomation.factorypmi.application.components.util.FPMILayout._getPrefSize(FPMILayout.java:159)
	at com.inductiveautomation.factorypmi.designer.xmlserialization.DefaultComponentDelegate.serializeComponentCommons(DefaultComponentDelegate.java:76)

Any hints what methods or properties does it try to read that returns null:

com.inductiveautomation.ignition.client.util.gui.ErrorUtil - Error Saving the container
com.inductiveautomation.ignition.common.xmlserialization.SerializationException: Error during serialization for property 'contentPane' on object '[FPMIWindow]TestMainWindow'
	at com.inductiveautomation.ignition.common.xmlserialization.serialization.DefaultObjectSerializationDelegate.serializeProperties(DefaultObjectSerializationDelegate.java:307)
	at com.inductiveautomation.factorypmi.designer.xmlserialization.WindowDelegate.serialize(WindowDelegate.java:34)
	at com.inductiveautomation.ignition.common.xmlserialization.serialization.XMLSerializationContext.serialize(XMLSerializationContext.java:49)
	at com.inductiveautomation.ignition.common.xmlserialization.serialization.XMLSerializer.serialize(XMLSerializer.java:525)
	at com.inductiveautomation.ignition.common.xmlserialization.serialization.XMLSerializer.serializeBinary(XMLSerializer.java:507)
	at com.inductiveautomation.factorypmi.designer.workspace.WindowWorkspace.saveContainer(WindowWorkspace.java:1823)
	at com.inductiveautomation.factorypmi.designer.workspace.WindowWorkspace.commitDesignable(WindowWorkspace.java:1539)
	at com.inductiveautomation.factorypmi.designer.workspace.WindowWorkspace.commitAll(WindowWorkspace.java:1096)
	at com.inductiveautomation.factorypmi.designer.model.VisionDesignerImpl.notifyProjectSaveStart(VisionDesignerImpl.java:620)
	at com.inductiveautomation.ignition.designer.IgnitionDesigner.commitAll(IgnitionDesigner.java:1608)
	at com.inductiveautomation.ignition.designer.IgnitionDesigner.handleSave(IgnitionDesigner.java:1373)
	at com.inductiveautomation.ignition.designer.IgnitionDesigner$Handler.handleSaveAction(IgnitionDesigner.java:2820)
	at com.inductiveautomation.ignition.designer.IgnitionDesigner$Handler$5.actionPerformed(IgnitionDesigner.java:2400)
	at java.desktop/javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
	at java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
	at java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
	at java.desktop/javax.swing.DefaultButtonModel.setPressed(Unknown Source)
	at java.desktop/javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
	at com.jidesoft.plaf.basic.BasicJideButtonListener.mouseReleased(Unknown Source)
	at java.desktop/java.awt.AWTEventMulticaster.mouseReleased(Unknown Source)
	at java.desktop/java.awt.Component.processMouseEvent(Unknown Source)
	at java.desktop/javax.swing.JComponent.processMouseEvent(Unknown Source)
	at java.desktop/java.awt.Component.processEvent(Unknown Source)
	at java.desktop/java.awt.Container.processEvent(Unknown Source)
	at java.desktop/java.awt.Component.dispatchEventImpl(Unknown Source)
	at java.desktop/java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.desktop/java.awt.Component.dispatchEvent(Unknown Source)
	at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
	at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
	at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
	at java.desktop/java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.desktop/java.awt.Window.dispatchEventImpl(Unknown Source)
	at java.desktop/java.awt.Component.dispatchEvent(Unknown Source)
	at java.desktop/java.awt.EventQueue.dispatchEventImpl(Unknown Source)
	at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
	at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
	at java.desktop/java.awt.EventQueue$5.run(Unknown Source)
	at java.desktop/java.awt.EventQueue$5.run(Unknown Source)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
	at java.desktop/java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.run(Unknown Source)
Caused by: java.lang.IllegalArgumentException: null
	at com.inductiveautomation.factorypmi.application.components.util.FPMILayout._getPrefSize(FPMILayout.java:159)
	at com.inductiveautomation.factorypmi.designer.xmlserialization.DefaultComponentDelegate.serializeComponentCommons(DefaultComponentDelegate.java:76)
	at com.inductiveautomation.factorypmi.designer.xmlserialization.DefaultComponentDelegate.serializeProperties(DefaultComponentDelegate.java:110)
	at com.inductiveautomation.ignition.common.xmlserialization.serialization.DefaultObjectSerializationDelegate.serialize(DefaultObjectSerializationDelegate.java:334)
	at com.inductiveautomation.ignition.common.xmlserialization.serialization.XMLSerializationContext.serialize(XMLSerializationContext.java:49)
	at com.inductiveautomation.ignition.common.xmlserialization.serialization.DefaultObjectSerializationDelegate.writeCall(DefaultObjectSerializationDelegate.java:240)
	at com.inductiveautomation.factorypmi.designer.xmlserialization.BasicContainerDelegate.serializeProperties(BasicContainerDelegate.java:120)
	at com.inductiveautomation.factorypmi.designer.xmlserialization.BasicContainerDelegate.serialize(BasicContainerDelegate.java:45)
	at com.inductiveautomation.ignition.common.xmlserialization.serialization.XMLSerializationContext.serialize(XMLSerializationContext.java:49)
	at com.inductiveautomation.ignition.common.xmlserialization.serialization.DefaultObjectSerializationDelegate.writeCall(DefaultObjectSerializationDelegate.java:240)
	at com.inductiveautomation.ignition.common.xmlserialization.serialization.DefaultObjectSerializationDelegate.writeCall(DefaultObjectSerializationDelegate.java:227)
	at com.inductiveautomation.ignition.common.xmlserialization.serialization.DefaultObjectSerializationDelegate.doProperty(DefaultObjectSerializationDelegate.java:284)
	at com.inductiveautomation.ignition.common.xmlserialization.serialization.DefaultObjectSerializationDelegate.serializeProperties(DefaultObjectSerializationDelegate.java:305)
	... 49 common frames omitted

Many thanks!

FPMILayout uses a Swing client property on the component to hold its UI information, including the preferred size. There probably something missing in your constructor chain that would set the UI layout.

Unrelated to BeanInfo.

Hi Pturmel,

Thank you for your quick respond! How to understand javadoc info on FPMILayout then:

Normal JComponent: Uses Jcomponent's preferred size property as the Dimension, and the FPMI_LC's preferred location Point to create the rectangle.
Bounds2DComponent: Uses the originalBounds property.

How/where do I need to add such originalBounds property since it is Bounds2DComponent, to comply with above?

Thank you!

No further clue.

Hi Bohdan,

I've been building my components by extending the AbstractVisionShape class, which itself is an extension of AbstractVisionComponent and it also implements the Bounds2DComponent interface. Maybe that's the way to go for you as well, instead of trying to implement Bounds2DComponent yourself.

As to your question here:

I add the boundingRect property just like any other property:

addProp("boundingRect",
		"Bounds",
		"Bounding rectangle of the component.",
		CAT_POSITION,
		HIDDEN_MASK);

I use the HIDDEN_MASK, because there is no need for this property to be visible. It is only necessary for serialization.

1 Like

Hi Mathias,

Many thanks for your reply! I did followed your advice and slightly adapted my code to extend AbstractVisionShape instead. And it saves fine now with no errors! And indeed boundingRect property did the trick too (tried with and without it) to properly remember/save location of my component.

To Pturmel: Also would like to add (in case if anybody else will encounter such issue) that Pturmel was also correct that serialization exception that I had when saving my original Bounds2DComponent was apparently not related to missing property on BeanInfo (although needed to properly save/load location/size of the component), since even without it AbstractVisionShape version (unlike for the case of just implementing Bounds2DComponent) saves fine with no serialization errors (it just won't remember correct bounds if without boundingRect property).

So that is sorted!
Thank you guys for your help!

3 Likes