A Smoother Change to Version 2.0
by Marc de Graauw
|
Pages: 1, 2, 3
Adding an IgnoreUnknown Element
For version 2 of the language, we'll add an <packaging> element. This is the advised packaging of the medicine. Understanding it is not required; apothecaries are specialized enough to select the best packaging, and the element contains merely an advisement, not a prescription:
# Language version 2
# added administration, mustUnderstand = false
element message {
attribute version { xsd:integer },
element require {
element version { xsd:integer }+
},
element prescription {
element medication { xsd:string },
element amount { xsd:integer },
element packaging { xsd:string }?
}
}
The change in language 2 -- L2 for short -- is backward-compatible: since the <packaging> element is optional, any L1 document (such as the one above) will be valid in L2. Because the language is backward-compatible, L2 receivers have the capability to understand L1 and L2 messages. L2 also gets its own stylesheet for ignoring unknown tags; this one will also retain the new packaging element.
L2 capabilities: read L1 L2 write L2
An L2 instance may contain the new element:
<?xml version="1.0" encoding="UTF-8"?>
<message version="2">
<require>
<version>1</version>
<version>2</version>
</require>
<prescription>
<medication>aspirin</medication>
<amount>24</amount>
<packaging>box</packaging>
</prescription>
</message>
The L2 instance lists the receivers that may process this message: version 1 and version 2 receivers. If an L1 receiver gets this message, it will conclude that it is safe to process this message. It will then do its "ignore unknown" magic and remove unknown elements, which will yield message version 1 above. We thus have the desired forward-compatibility.
Applying MustUnderstand Semantics
Next we'll go for an element that must be understood. We'll expand the language and enable the physician to instruct the apothecary to send the medication by mail to the patient's home address. We'll introduce an <element delivery { "mail" | "standard" }?. The receiver must understand this element, otherwise the medication would never be sent to the patient. However, the element is optional, so understanding is obviously only necessary when the element is present. We now get two flavors of instances:
<?xml version="1.0" encoding="UTF-8"?>
<message version="3">
<require>
<version>3</version>
</require>
<prescription>
<medication>aspirin</medication>
<amount>24</amount>
<delivery>mail</delivery>
</prescription>
</message>
The first flavor does have the <delivery> element. The only receivers that may process it are version 3 receivers. Version 1 and 2 receivers will recognize that they are not allowed to process it, and must return an error. It amounts to the same as a SOAP-style "mustUnderstand" flag on the <delivery> element, but without the need for such flags on every element that must be understood.
The second flavor does not have the delivery element:
<?xml version="1.0" encoding="UTF-8"?>
<message version="3">
<require>
<version>1</version>
<version>2</version>
<version>3</version>
</require>
<prescription>
<medication>aspirin</medication>
<amount>24</amount>
</prescription>
</message>
It basically is the same message 1 again. Receivers that support either L1, L2, or L3 may process it. This highlights a principle that every writer application should adhere to: maintain a list of versions that may consume the produced instance. For L3, the default list is L1, L2, L3. But whenever a <delivery> element is inserted, the list should be restricted to the L3-level receivers minimum.
Removing Obsolete Features
Sometimes backward-incompatible changes are made. A common case is when an ill-conceived part is replaced by a better alternative; the original is marked as "obsolete" for some versions, then removed. Such a removal is not backward-compatible. Let's take a detailed look:
<?xml version="1.0" encoding="UTF-8"?>
<message version="1">
<require>
<version>4</version>
</require>
<prescription>
<medication>aspirin</medication>
<quantity unit="pcs">24</quantity>
</prescription>
</message>
Now of course the idea of an <amount> element was ill-conceived. Not all medication comes in countable pieces. Sometimes prescriptions are in milliliters or milligrams. So we decided to remove amount and introduce <quantity>, with a unit attribute. We won't remove the obsolete <amount> element after several versions -- we'll do it right away. Receivers now must support L4: if older processors try to process the message, amount would lack and quantity would be stripped, making the prescription incomplete.
Furthermore, version 4 of the language could refuse to accept documents with the <amount> element:
L4 capabilities: read L4 write L4
In this case, messages from older senders would be rejected with an "Obsolete version, please upgrade" error. This seems a bit harsh for the <amount> example, but if a security leak were discovered in the older versions, such a policy would be advisable for sensitive messages. And even for simple features, after enough time it makes sense to require all parties in a professional environment to support a specific minimum level of a language specification.
Attributes and Code Lists
The Capability Compatibility Design Pattern supports not only new and removed elements, but attributes as well. Of course it depends a bit on the "ignore unknown" implementation, but supposing we remove not only unknown elements, but unknown attributes in known elements as well. The mechanics for attributes are no different than those sketched above. What's more, we can require support for some version of the language based on the code value in an enumeration. Above, we introduced the <delivery> tag:
element delivery { "mail" | "standard" }?
We can change it to:
element delivery { "mail" | "standard" | "personal" | "any" }?
Now "any" could mean it's up to the apothecary to decide how to deliver the medication. The value can safely be ignored by older processors.
<?xml version="1.0" encoding="UTF-8"?>
<message version="5">
<require>
<version>4</version>
<version>5</version>
</require>
<prescription>
<medication>aspirin</medication>
<quantity unit="pcs">24</quantity>
<delivery>any</delivery>
</prescription>
</message>
The stylesheet for version 4 will remove the <delivery> element with the unknown value. But the value "personal" means the physician insists that the medication may only be given to the patient in person, not to anybody else. This value may not be ignored, so version 5 processors should require a minimum of version 5 whenever they insert the personal value:
<?xml version="1.0" encoding="UTF-8"?>
<message version="5">
<require>
<version>5</version>
</require>
<prescription>
<medication>aspirin</medication>
<quantity unit="pcs">24</quantity>
<delivery>personal</delivery>
</prescription>
</message>
Namespaces and multiple languages in a single document make things more complicated than can be shown here, but the same principles apply.
Conclusions
The Capability Compatibility Design Pattern is a very flexible and powerful way to control changes in versions of languages for exchanges over the Internet. It goes beyond SOAP-style mustUnderstand headers and easily supports IgnoreUnknown and mustUnderstand semantics for elements, attributes, and enumerations. It does this all by adhering to two simple principles:
- List all versions, including older ones, that you know may process your message inside the message you make.
- Know all versions you support, and check whether they fit requirements in incoming messages.
- Version is not capability...
2007-04-29 06:00:41 xalkina - Version is not capability...
2007-05-08 07:15:32 Marc de Graauw