PropertyFiles
From APIDesign
(→Ready for Evolution) |
|||
(6 intermediate revisions not shown.) | |||
Line 1: | Line 1: | ||
Many systems store their configuration in some form of [[PropertyFiles|property files]]. Files which contain string ''key/value'' mappings. This is indeed an example from the [[APITypes]], as changes to these values influence behaviour of those systems. This may seem much more trivial form of an [[API]] than [[Java]] classes and their signatures and in some way it is, however the rules of proper [[APIDesignPatterns|API Design]] are still applicable: one needs to keep [[BackwardCompatibility]], be ready for [[API]] [[evolution]], etc. | Many systems store their configuration in some form of [[PropertyFiles|property files]]. Files which contain string ''key/value'' mappings. This is indeed an example from the [[APITypes]], as changes to these values influence behaviour of those systems. This may seem much more trivial form of an [[API]] than [[Java]] classes and their signatures and in some way it is, however the rules of proper [[APIDesignPatterns|API Design]] are still applicable: one needs to keep [[BackwardCompatibility]], be ready for [[API]] [[evolution]], etc. | ||
+ | |||
+ | ===== Bundle Manifest Troubles ===== | ||
One commonly used ''property file'' in [[Java]] is the [[JAR]] manifest. It contains ''tag/value'' mappings for one main and many per entry sections. Many systems including [[NetBeans Runtime Container]] or [[OSGi]] recognize values of their own tags and based on them prepare runtime environment for the classes contained in the [[JAR]] file. Recently I was playing a bit with [[Felix]] and was trying to create a bundle (a [[JAR]] with [[OSGi]] manifest) that will require another bundle: | One commonly used ''property file'' in [[Java]] is the [[JAR]] manifest. It contains ''tag/value'' mappings for one main and many per entry sections. Many systems including [[NetBeans Runtime Container]] or [[OSGi]] recognize values of their own tags and based on them prepare runtime environment for the classes contained in the [[JAR]] file. Recently I was playing a bit with [[Felix]] and was trying to create a bundle (a [[JAR]] with [[OSGi]] manifest) that will require another bundle: | ||
Line 19: | Line 21: | ||
</source> | </source> | ||
- | After adding it, everything started to behave as expected. The bundle is now rejected, as the ''Require-Bundle'' dependency cannot be satisfied. It is easy to use an [[API]] if you resolve your problems by asking author of the specification. However this is probably not really scalable and there | + | After adding it, everything started to behave as expected. The bundle is now rejected, as the ''Require-Bundle'' dependency cannot be satisfied. It is easy to use an [[API]] if you resolve your problems by asking author of the specification. However this is probably not really scalable and there seems to be something wrong with the [[OSGi]] manifest [[API]]. |
+ | |||
+ | ===== Ready for [[Evolution]] ===== | ||
+ | |||
+ | An important rule when designing an [[API]] is to prepare it for [[evolution]]. Why? Because first version is never perfect, you will always need to release subsequent ones to fix bugs and provide new enhancements. | ||
+ | |||
+ | It is sometimes hard to remind ourselves of the need for [[evolution]], especially when we are about to release the first version. We feel we did our best. We believe we created the most ingenious [[API]] on the Earth. It is hard to remind ourselves that the same [[API]] is also supposed to be ''imperfect''. This requires a little bit of [[Doublethink]]. Still we need to do it, otherwise we and our [[API]] users will be in deep problems in the future - as my [[OSGi]] manifest adventure shows. | ||
+ | |||
+ | When the [[OSGi]] team designed its first specification, it did the best possible job. It defined few manifest tags and described how they shall be interpreted, it also prescribed that unknown tags (for example those provided by other systems) shall be ignored. So far so good, it is always easy to create the first version... | ||
+ | |||
+ | However later they needed to add the '''Require-Bundle''' tag. At that point it became clear that there is need for [[evolution]]. It is necessary to indicate whether the [[API]] user wants to use new [[OSGi]] specification (which understands the enhanced set of tags) or the old one (where '''Require-Bundle''' has no meaning). What can we do? Well, we need to version the property/manifest file! That is why the specification now defines the '''Bundle-ManifestVersion''' tag. Since now, every new specification release which adds new tags will boost the manifest version number to let the [[API]] user properly indicate which set of tags shall be recognized. | ||
+ | |||
+ | However, it is too late! As the '''Bundle-ManifestVersion''' tag was not introduced in the first version of the specification, it can only be optional, not required. If it is missing, the [[OSGi]] framework cannot refuse the [[JAR]], it needs to assume that it is old [[JAR]] written against the first version. Sometimes the assumption is wrong, and then poor users like me (who forget to add '''Bundle-ManifestVersion''' tag) cannot stop wondering what is going on! | ||
+ | |||
+ | If the specification was ready for [[evolution]] since its first version, it would require presence of | ||
+ | |||
+ | <source lang="text"> | ||
+ | Bundle-ManifestVersion: 1 | ||
+ | </source> | ||
+ | |||
+ | in each bundle. Without this tag, the bundle would be rejected. The [[API]] users would be forced to specify which version they are wanting to use and all my troubles described above would be prevented. | ||
+ | |||
+ | What is the takeaway? When you design an [[API]] based on [[PropertyFiles]], don't forget to include a version identifier in it. Only then your [[API]] becomes ready for [[evolution]]. | ||
+ | |||
+ | ===== [[Gradle]] [[GradleWrapper|Wrapper]] ===== | ||
+ | |||
+ | We can expand the here-in gained conclusion to [[Gradle]]: If you want to be sure your [[Gradle]] projects build in a year or two then make sure to always include the [[GradleWrapper]] and exactly specify the [[Gradle]] version! | ||
+ | |||
+ | |||
+ | [[Category:APIDesignPatterns]] | ||
+ | [[Category:APIDesignPatterns:Evolution]] | ||
+ | |||
+ | = [[Talk:PropertyFiles|Comments]]? = | ||
+ | {{:Talk:PropertyFiles}} | ||
[[Category:APITypes]] | [[Category:APITypes]] |
Current revision
Many systems store their configuration in some form of property files. Files which contain string key/value mappings. This is indeed an example from the APITypes, as changes to these values influence behaviour of those systems. This may seem much more trivial form of an API than Java classes and their signatures and in some way it is, however the rules of proper API Design are still applicable: one needs to keep BackwardCompatibility, be ready for API evolution, etc.
Contents |
Bundle Manifest Troubles
One commonly used property file in Java is the JAR manifest. It contains tag/value mappings for one main and many per entry sections. Many systems including NetBeans Runtime Container or OSGi recognize values of their own tags and based on them prepare runtime environment for the classes contained in the JAR file. Recently I was playing a bit with Felix and was trying to create a bundle (a JAR with OSGi manifest) that will require another bundle:
Manifest-Version: 1.0 Require-Bundle: does.no.exists;bundle-version="[1.0,2.0)" Export-Package: org.bar Bundle-Version: 1.1.0 Bundle-SymbolicName: org.bar
As the bundle does.no.exists does not exists, I would expect the system to refuse to start the org.bar bundle. To my biggest surprise, my bundle was successfully started. I've spend few days trying to find out what is wrong. Did I make a typo? No. Did I read the OSGi specification incorrectly? Neither. Is Felix broken? Let's debug it! Or rather not, this is not cluelessness! I do not want to be an expert at Felix, I am just an OSGi API user! So what is wrong?
At the end I decided to email Richard Hall, Felix maintainer and send him my org.bar bundle. In a minute I got an answer back, as he spotted the problem immediately, the following tag was missing:
Bundle-ManifestVersion: 2
After adding it, everything started to behave as expected. The bundle is now rejected, as the Require-Bundle dependency cannot be satisfied. It is easy to use an API if you resolve your problems by asking author of the specification. However this is probably not really scalable and there seems to be something wrong with the OSGi manifest API.
Ready for Evolution
An important rule when designing an API is to prepare it for evolution. Why? Because first version is never perfect, you will always need to release subsequent ones to fix bugs and provide new enhancements.
It is sometimes hard to remind ourselves of the need for evolution, especially when we are about to release the first version. We feel we did our best. We believe we created the most ingenious API on the Earth. It is hard to remind ourselves that the same API is also supposed to be imperfect. This requires a little bit of Doublethink. Still we need to do it, otherwise we and our API users will be in deep problems in the future - as my OSGi manifest adventure shows.
When the OSGi team designed its first specification, it did the best possible job. It defined few manifest tags and described how they shall be interpreted, it also prescribed that unknown tags (for example those provided by other systems) shall be ignored. So far so good, it is always easy to create the first version...
However later they needed to add the Require-Bundle tag. At that point it became clear that there is need for evolution. It is necessary to indicate whether the API user wants to use new OSGi specification (which understands the enhanced set of tags) or the old one (where Require-Bundle has no meaning). What can we do? Well, we need to version the property/manifest file! That is why the specification now defines the Bundle-ManifestVersion tag. Since now, every new specification release which adds new tags will boost the manifest version number to let the API user properly indicate which set of tags shall be recognized.
However, it is too late! As the Bundle-ManifestVersion tag was not introduced in the first version of the specification, it can only be optional, not required. If it is missing, the OSGi framework cannot refuse the JAR, it needs to assume that it is old JAR written against the first version. Sometimes the assumption is wrong, and then poor users like me (who forget to add Bundle-ManifestVersion tag) cannot stop wondering what is going on!
If the specification was ready for evolution since its first version, it would require presence of
Bundle-ManifestVersion: 1
in each bundle. Without this tag, the bundle would be rejected. The API users would be forced to specify which version they are wanting to use and all my troubles described above would be prevented.
What is the takeaway? When you design an API based on PropertyFiles, don't forget to include a version identifier in it. Only then your API becomes ready for evolution.
Gradle Wrapper
We can expand the here-in gained conclusion to Gradle: If you want to be sure your Gradle projects build in a year or two then make sure to always include the GradleWrapper and exactly specify the Gradle version!
Comments?
Wayne Beaton said ...
Richard S. Hall said ...
Actually, the real reason we had to introduce Bundle-ManifestVersion is because we changed the semantics of the existing Export-Package header (in R3 it also implied an Import-Package, but in R4 it does not). Otherwise, I agree it would have been smarter to include the manifest version from the beginning. :-)
--Richard S. Hall 15:25, 16 December 2008 (CET)
Richard S. Hall said ...
Also, Wayne makes a good point. BND will also "verify" bundles, but I am not sure if it would have pointed out the issue in this case. Of course, the moral to this story is, don't use Require-Bundle, use Import-Package instead. ;-)
--Richard S. Hall 15:27, 16 December 2008 (CET)
Chris Aniszczyk said ...
If you used any OSGi-related tools like Eclipse PDE... this would catch the problem immediately.
Your point about including version identifiers is very valid and people should learn to take it in heart. It's a common mistake people forget about.
--Chris Aniszczyk 16:03, 16 December 2008 (CET)
JaroslavTulach said ...
Somewhere in TheAPIBook, I guess it is in Chapter 9, I mentioned that one of the best APIs is wizard. Good wizard can turn any API, regardless how bad, into perfectly shining, beautiful star. I am sure that if I used PDE, I would avoid falling into the ManifestVersion trap.
--JaroslavTulach 14:39, 19 December 2008 (CET)
Ansgar Konermann said ...
Hi Jaroslav,
what if a developer accidentally specifies a wrong Bundle-ManifestVersion value? The Require-Bundle tag would still be ignored!
To me, merely making the Bundle-ManifestVersion header mandatory from version 1 on does not help very much in practice.
Instead, the user of the API should be *informed* if the service providing this API detects "inconsistent use" of different versions of the API. In this case: the OSGi container or bundling tool should warn at packaging or deployment time if one tries to create/use a bundle without Bundle-ManifestVersion specified, but containing tags from "non-1.0" API versions inside the bundle manifest.
This is similar to a "syntax check" done by programming language compilers. If our OSGi tooling does not support this syntax check (yet?), at least the runtime container should do.
The point I'm trying to make is: let's not blame the API *specification* for a *missing mechanism* which can ensure that the API is used correctly.
Best regards
Ansgar
--Ansgar Konermann 17:34, 25 November 2010 (CET)
Hello Ansgar, I think you are right. The most important thing is to keep the user informed. Probably the OSGi spec, tools, impls could do better job. However my point is that having the version identification attribute present (and required) since first version makes your life easier. This is not that visible in case of PropertyFiles, where it is relatively easy to add new attribute in (almost ~ 99%) compatible way. However in case of binary formats, it is essential to get the version right as soon as possible, otherwise one can completely loose offset and interpret the content completely incorrectly. Without knowing that 3rd byte of the file represents a version, it is really tough job.
Btw. your fix is not 100% compatible: In old version I might accidently use one of the (in future) added attributes for completely unrelated purposes. If I then upgrade my OSGi container to new version, it will complain about incorrect syntax (or whatsoever) and refuse my bundle which used to be completely fine in previous version. I guess this is the reason why new OSGi specification requires the Bundle-Version attribute to be present. Guys wanted to be 100% compatible and traded that for keeping user "informed" (yes, they still could do a better job and at least print a warning).
Thanks for your comment. --JaroslavTulach 18:51, 28 November 2010 (UTC)
Assovorge said ...
Hello. And Bye.
--Assovorge 19:54, 17 February 2012 (CET)
FWIW, The Eclipse Plug-in Development Environment (PDE) has tools for configuring OSGi manifests. And it works with Felix.
--Wayne Beaton 14:54, 16 December 2008 (CET)