Showing posts with label osgi. Show all posts
Showing posts with label osgi. Show all posts

Wednesday, June 30, 2010

Extension system when you cannot depend on OSGi?

I've been developing with OSGi for so long that sometimes I forget that not everyone in the Java world can take advantage of all the benefits offerred by it. Recently I have been contemplating the approaches to framework extensibility when you cannot depend on OSGi. Note that I am not looking for other solutions to modularity. I am looking for an extension contribution system that is not specific to OSGi. We'd like to support Sapphire in all Java UI contexts and extensibility for implementing new UI parts, renderers, etc. is key.

Before I start re-inventing the wheel... Have others faced similar requirements? If so, what was the approach that you chose?

Sunday, March 1, 2009

Count me out from p2 fan club

I don’t make habit of ranting about technology, but p2 has been driving me up the wall. The old update manager may not have been perfect, but at least it didn’t have the bad habit of preventing installation cases that should work from working.

So we are putting finishing touches on a new version of Oracle Enterprise Pack for Eclipse (OEPE) and it’s time to test various installation scenarios. Eclipse Ganymede SR2 was also just released last week, so we are verifying compatibility with it. One of the basic installation scenarios that we are testing starts out with an all-in-one kit that includes Eclipse Ganymede GA with the previous version of OEPE. The second step in the scenario is to update all of the Eclipse components in that installation. The new version of OEPE requires at least SR1. I let Eclipse search for updates and install everything that it finds. That works. Presumably at this point, the installation should be equivalent to a fresh Eclipse Ganymede SR2 install. The final step is to install the new version of OEPE from a local update site. P2 thinks for a while and then says there are problems that will prevent the installation from working and refuses to go forward.

WTF? I know perfectly well that the plugins we are installing are compatible with Ganymede SR2. They’ve been built using SR2 as the target platform and they work just fine when simply added to the Eclipse installation. Now what? Taking a look at the problems reported by p2, I find about a hundred messages that look like the following.

Cannot find a solution where both "bundle org.eclipse.wst.validation [1.2.0,1.3.0)" and "bundle org.eclipse.wst.validation [1.1.0,1.2.0)" are satisfied.
Unsatisfied dependency: [org.eclipse.jst.ws.axis.consumption.core 1.0.204.v200708151945] requiredCapability: osgi.bundle/org.eclipse.core.resources/[3.2.0,3.4.0)
Unsatisfied dependency: [org.eclipse.jst.ws.axis.consumption.ui 1.0.204.v200801222138] requiredCapability: osgi.bundle/org.eclipse.core.resources/[3.2.0,3.4.0)
Unsatisfied dependency: [org.eclipse.wst.ws 1.0.204.v200711140435] requiredCapability: osgi.bundle/org.eclipse.core.resources/[3.2.0,3.4.0)
Unsatisfied dependency: [org.eclipse.wst.command.env.ui 1.0.203.v200709052219] requiredCapability: osgi.bundle/org.eclipse.core.resources/[3.2.0,3.4.0)

These messages don’t make a whole lot of sense. None of them reference plugins that I am trying to install and there is no hint in the messages regarding what actually caused the problem. I looked at one of them in detail to make sure I wasn’t missing something obvious. The second message says that a dependency of org.eclipse.jst.ws.axis.consumption.core plugin cannot be found. That makes sense since it’s a very old version of the plugin that’s not compatible with Ganymede. The question is why p2 trying to resolve that plugin? I look at my installation and I see versions 1.0.304.v200805140230 and 1.0.306.v200810082309. That makes sense. Those versions correspond to what shipped with Ganymede GA and Ganymede SR2. I do not find version 1.0.204.v200708151945 that p2 is complaining about anywhere.

At this point, I gave up trying to make sense of the problem messages and proceeded to blindly try various changes to the way the OEPE update site is constructed to see if I would get a different result. Two alternatives I tried made this scenario work: (a) removing all version constraints from plugin dependencies and (b) reverting to the old-style update site with a site.xml file and no p2 metadata. We still need to do some more testing, but we probably will go with (b) and give up on p2-enabling our update site.

I had reservations about p2 since the poor way in which it was rolled out roughly a year ago, but after a year of fighting with it and this recent experience, I can honestly say (without an ounce of exaggeration) that p2 is the worst regression ever introduced into the Eclipse Platform. I understand the problems that p2 is supposed to solve, but there is just no excuse for destroying the most basic of core scenarios in the process. If it wasn’t ready for Ganymede, it should have stayed as an incubator for a while longer.

Sunday, February 1, 2009

Better way to manage dependency version ranges

OSGi provides an extremely powerful and precise mechanism for controlling acceptable version ranges when specifying dependency on bundles or packages. In theory (as described by policies of various projects at Eclipse), the developer would take into account his plugin’s API and behavior needs, cross-reference that with version information about the bundle in question and carefully craft the version range in dependency declaration to accurately reflect his plugin’s actual needs while leaving the version range as open as possible to allow users maximum flexibility when composing an installation. Further, in theory, the developer should be continuously aware of dependency version ranges specified in his product’s various plugins and how they correlate to functionality exposed by those plugin. As development progresses, the developer is supposed to be able to spot when he started depending on functionality that’s not available in the specified min version and reset the min version accordingly.

That’s the theory. In practice I haven’t met a single developer with sufficient time on their hands or sufficient mental capacity to keep all of the necessary information in his head at all times in order to properly apply this policy. What I’ve seen happen most often is that the min range gets set based on whatever the plugin version happens to be at the time the dependency is first added. PDE helpfully inserts this information in your manifest by default. The max version then gets set by applying a team policy (typically by bumping up either the major or the minor version). This happens when dependency is first introduced. As the code continues to evolve, the min version is typically not touched again. The max version is incremented when the build gets broken by the dependency bumping up their versions past a certain point. The cycle repeats.

After many years of observing this situation, I am convinced that having developers manage version ranges creates a lot of overhead and does not yield satisfactory results no matter how hard people try.  To me, dependency version ranges are most useful when you have shipped your product in binary form. When taken collectively across a component (collection of bundles), they represent a statement of what your team is willing to support as a working configuration. Ideally, this information should be consistent across plugins and as accurate as possible.

Any time you talk about setting version ranges, you are considering three versions:

  1. The version that you developed and did most of your testing with. I call this the “target version”. Typically, this is what you would list as recommended configuration in your documentation.
  2. The minimum version that you are willing to support. The level of testing you can afford to allocate to this version is bound to be less than what you would allocate for the target version, so there is a certain amount of risk that an undetected issue is going to slip through. The further back you go from the target version when setting the minimum version, the greater your risk.
  3. The maximum version that you are willing to support. Since this version will typically not exists at the time of your ship date, setting this version involves an educated guess based on understanding of what policies your dependencies use when incrementing their versions and the degree to which you are relying on undocumented (internal) code and behaviors. The spread between the target version and the maximum version is where you highest risk lies. On one hand you’d like to assure long viability of your release in the field. On the other hand, the further out you go, the greater the risk that your product will not work and make a liar out of you in the eyes of your users.

Because getting the above version decisions right and consistent across a component is extremely important, it is not a good idea for individual developers to be making these decisions on a plugin-by-plugin basis. In an Open Source environment, this should be a component-wide decision made collectively by the committers. In a commercial environment, this decision is often made higher up in the organization based on availability of resources and target user base considerations.

When the overall decision is made, it is typically expressed in broad terms. For instance… “this version will ship on Ganymede SR1, but should work with all versions of Ganymede starting with GA”. It is then up to developers to translate that requirement into version ranges in the manifest.

That’s a ton of tedious manual work with lots of room for mistakes. In other words, a perfect candidate for automation. A few years ago, I wrote a set of two custom Ant tasks to automate this process. The first task reads an Eclipse installation and produces an inventory file that lists id and version of every bundle found. The second tasks takes as input an inventory file representing the minimum platform, an inventory file representing the target platform and a policy for setting the maximum versions.  For every dependency, the task looks up the version from the minimum platform inventory. That becomes the left-hand-side of the version range. It then looks up the bundle version in the target platform inventory and applies the policy function to it. Here are some examples of policy functions: “x.y.z ->x+1.0.0”, “x.y.z ->x.y+1.o” or the extremely conservative “x.y.z ->x.y.z+1”. You can set different policies for different plugins or components based on what you know of their versioning conventions. The version returned by the policy function becomes the right-hand-side of the version range.

We have been using these two tasks to automate and improve the quality of our version ranges for several releases of Eclipse tooling products at BEA and now at Oracle. Developers don’t set the versions on the dependencies specified in the bundle manifests stored in the source repository. At the end of every build, a process runs that splices version ranges into the manifests just prior to packaging the bundles for distribution. The target inventory is always generated on the fly based on whatever the product is building against. The minimum platform inventory is generated once when the minimum platform decision is made. The inventory is then stored in the source repository.

This has been an extremely useful process improvement for us. Not only do we have more confidence in the version ranges encoded in our product distributions, but it takes significantly less work for developers to manage all of this. The developers never have to think about dependency versions during normal course of development and integrating new versions of dependencies takes less work (since version ranges in manifest don’t have to be fixed manually to get the build to work).