State Won’t Cause Problems, Will It?
In the previous discussion about migration to gXML, I explored the conversion of one simple stateless method. That post explored some of the general patterns of migrating code to use gXML, by way of deprecating old methods and adding new ones.
With this discussion, I want to take up a much more tangled scenario – a class that holds state, and how to migrate that.
Parallel Tracks
I had the most trouble with the ElementProxy class. This class has a protected field in it of type Element. A search of the existing source quickly revealed quite a number of subclasses that directly refer to this field. To change the class to work with gXML, though I needed to make sure that the class ElementProxy ended up being parameterized by <N>, and that a field corresponding to this existing field had to be of type N, rather than of type Element. Remember, though, that my goal is to migrate the library incrementally – existing classes have to operate unmodified, at least until I have a chance to convert them. So I took an approach of having parallel tracks. Where I started with this:
protected Element _constructionElement;
… I ended up with this:
/** @deprecated **/
protected Element _constructionElement;
private N _wrappedElement;
private N _lastSyncedElement;
public void setElementNode(N elem) { ... }
public N getElementNode() { ... }
Before explaining this further, perhaps I have to explain the two scenarios more fully. In the easy case, a given subclass has been fully modified to use the new gXML-based approach, and that class uses new getter/setter methods I defined. This easy scenario works, because all access goes through the the get and set methods. In the harder case, existing clients directly set and get the old protected member that I want to replace. Unless I wanted to convert the entire class hierarchy stemming from this one class all at the same time, I needed a way to support a partially ported class hierarchy stemming from this one class.
In my first attempt at this, I didn’t have the “_lastSyncedElement” member. This didn’t work, though. Instead, I found code directly setting the protected member, and yet I had already rewritten the parent class to use the new private member via the getter method. Since Java doesn’t give me any way to detect when a protected member is altered, the value returned by the get method didn’t match the value directly set into the protected method, and tests failed. The solution, simple enough, was to add a third copy of the field, as show above, the “lastSyncedElement”. The third copy kept track of the last value set using the new API. Now, the “get” method, whenever it went to get the new private value, it would first check to see if the “last sync value” matched the value of the old protected field. If it didn’t, it would copy the value from the protected field into the new private member.
This change meant that all the as-yet-unmodified DOM-based code could run unmodified by directly accessing the “_constructionElement” field. All the new code would call the “set” method to set the value, and the object being set was of type Element, I would also set the original protected value, and its shadow – “_lastSyncedElement”. The new set and get methods look like this:
protected void setElementNode(N elem) {
_constructionElement = (elem instanceof Element) ? (Element) elem : null;
_lastSyncedElement = _constructionElement;
_wrappedElement = elem;
}
public N getElementNode() {
if (_constructionElement != _lastSyncedElement) {
_lastSyncedElement = _constructionElement;
_wrappedElement = (N) _constructionElement;
}
return _wrappedElement;
}
In this way, any direct changes to the old protected member would be detected and used by new code.
Tricks For Constructors
ElementProxy gave me another problem, too. It had existing constructors that took Element object as parameters. In the gXML world, that had to be turned into an element of type N, and an XmlContext<N> ctx, so that it would be possible to do something with the type N parameter. So similar to the approach used with the purely static methods I discussed in my previous writeup, I deprecated the existing constructor, and had it call a new constructor with a new DOM-specific context object.
That solved one problem, but another cropped up. Some subclasses of ElementProxy had a pair of constructors, each with a single parameter, but one taking Document, and one taking Element. In gXML, both Document and Element collapse to type N. This meant that a new constructor method taking a context and an N could no longer distinguish these two cases. As a somewhat silly solution, I changed it so that the “Document” form of the constructors took an extra “do nothing” boolean parameter (which I named “unusedDiscriminator”).
Perhaps A Different Approach?
I might have followed an alternate course. I could have wrapped the _constructionElement member with methods to access it. That is, I might have swept through the code base once to add calls to get/set methods that still used the Element type. This would have let me directly intercept all attempts to set the value. However, as I went through and converted code, I would have had to replace these method calls with ones that returned and set values of type <N>. I think this means that I would have needed to touch each source file twice – once to convert it to use the method wrapping access to an Element, and then to convert those calls to something wrapping <N>. In the end, I think the approach I took probably worked just as well, if not slightly better.
End Result
The end result of this careful backwards compatibility left me with unnecessary code. Once I converted all of the code – all of the subclasses of this class, and all of the direct uses of the protected field, I was left with no direct access to the protected field, and I have found I can now remove the parallel tracks. But the intermediate solution did let me worry about migrating code a file at a time, rather than a class hierarchy at a time. I think that made the extra complexity worth it.