Changing the type of an attribute serialized by XStream in Jenkins Plug-ins
This post was written while fixing JENKINS-23772 for the Image Gallery Plug-in. This plug-in can be used to create image galleries for artifacts found in the workspace in Jenkins.
What was suggested in JENKINS-23772, was that instead of accepting only integers for the width, that the plug-in started to accept text values as well. This way 10
, 10px
or 10%
as valid values. The challenge in user requests like this, is how to maintain backward compatibility in your plug-in, while releasing a new version that changes objects and attributes.
An ImageGallery implements the Descriptor/Describable pattern for Jenkins, and users can choose an implementation in the job configuration. The ImageGallery abstract class contains an imageWidth
Integer attribute, which is persisted on the disk by Jenkins, using XStream.
You can read more about retaining backward compatibility in this Jenkins Wiki page.
Changing the code
Our task is to change that attribute to String, make sure the behaviour is consistent in the image gallery implementations, and guarantee that Jenkins will not crash when trying to load jobs with old imageWidth
Integer attribute.
So first you have to make sure that your Serializable classes bump the serialVersionUID
value, and that your unit tests are still passing after your changes.
If we have data already persisted on the disk and being used by XStream, changing attributes may result in strange errors. In our case we would like to change an Integer attribute to String, and persist it again.
The solution in this case, is add the @Deprecated
annotation to the existing Integer field, add another String field with a different name, and implement the readResolve
method to load the String value from the Integer value, when necessary.
Remember to also move the @DataBoundConstructor
to your new constructors, and add @Deprecated
to the right fields, methods, classes, and so it goes.
|
|
Good. So now our code already supports our changes.
There are at least two places where the integer image width was being saved in our previous jobs: the ImageGallery implementation object, and the Action being saved for each build.
|
|
|
|
Now that we have made our changes in the code, and left the old fields deprecated, we have to tell XStream to use the new field when reading old entries like these.
|
|
What it does, basically, it tell our program to use the value of the Integer fields to create a new object, with the String fields that we just created. This way, old instances serialized onto the disk, will be deserialized and filled with the old values.
In other words, it will be transparent to users, no errors on the screen or logs, and we will have kept backward compatibility.
Just remember to review your code, make sure your Jelly is passing the right field names, you are not using the old value, and that everything seems to work.
Happy hacking!