blob: c729c0d303850c705ebe39245258d5d48df9eb9a [file] [log] [blame]
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
= DevFaqImplementationDependency
:jbake-type: wiki
:jbake-tags: wiki, devfaq, needsreview
:jbake-status: published
:keywords: Apache NetBeans wiki DevFaqImplementationDependency
:description: Apache NetBeans wiki DevFaqImplementationDependency
:toc: left
:toc-title:
:syntax: true
=== What is an implementation dependency and when should I use one?
Normally modules interact with one another using _public packages_: a module can (indeed, must) declare which, if any, of its Java packages are intended to be visible to other modules. When you declare a specification dependency on another module, you only get access to the public packages. This kind of dependency looks like this in the JAR manifest (which is normally constructed from `nbproject/project.xml` in sources):
[source,java]
----
OpenIDE-Module-Module-Dependencies: some.other.module > 1.5
----
(requesting version 1.5 or greater of `some.other.module`) or like this:
[source,java]
----
OpenIDE-Module-Module-Dependencies: some.other.module
----
(requesting any version; not recommended).
Occasionally you may find that the author of a module neglected to expose certain classes in public packages which you know (from reading the source code) that you need to use and know how to use properly. The classes are public but not in declared public packages. It is _possible_ to access these classes if you really have to. But you need to declare a dependency on that _exact_ version of the other module, since such classes might change incompatibly without notice in a newer copy of that module. Since such a change could break your module, the NB module system requires that you declare the implementation dependency so that it can verify _before_ loading your module that it matches the other module. The general idea is that if module B has an implementation dependency on module A, the system should not be able to load B unless it has the exact same version of A that B was compiled against. To make an implementation dependency in the manifest, use
[source,java]
----
OpenIDE-Module-Module-Dependencies: some.other.module = 3
----
where the "3" is what that other module declared as its current implementation version:
[source,java]
----
OpenIDE-Module-Implementation-Version: 3
----
In order to add an implementation dependency, first add the dependency to the project (e.g. click on "Add Module Dependency" from the "Libraries" node or by click the "Add Dependency..." button in Project->Properties->Libraries panel). Make sure you've checked the "Show Non-API Modules" checkbox when you're looking for the non-API module, otherwise you're not going to find it. Then, after you've added the module as a dependency, edit the dependency (either Project->Properties->Libraries->Select Dependency->Edit or Project->Right click on dependency Libraries node->Edit) and just select the "Implementation Version" radio box in the Edit dependency dialog. If you don't want to "see" all packages within the module, but only a subset, uncheck the "Include Packages in Classpath" checkbox and select the packages you want to see. This works best if the other module uses a nonnegative integer for the implementation version, and if you also check *Append Implementation Versions Automatically* in the properties dialog.
Implementation dependencies are to be avoided unless you _really_ need access to all the classes in another module, for the following reason: If your module has an implementation dependency on module A, and module A is upgraded, your module probably must be upgraded as well, or the system will not load it (assuming module A's implementation version has changed with the upgrade - it should have). It is a particularly bad idea to use implementation dependencies if you do not know what the other module's author's intentions are for keeping the classes you use available and compatible. It is always possible to make an enhancement request asking for the other module to make the classes you want to use available publicly. Do not use implementation dependencies just to have access to one or two some convenience or utility classes in another module - copy them instead, and file a bug report asking for an API for doing what you're trying to do.
==== Friend dependencies
_Friend dependencies_ are a little different. A module may have an API which its author is not yet comfortable exposing to just anyone - it might not be fully stabilized yet. In this case, the module with the API can declare some public packages, but also stipulate that only a predefined list of "friend modules" are permitted to use them. The friend modules just declare a regular specification version dependency, but unknown modules are not permitted to use any packages from the API module without an implementation dependency.
(Look at the *Versioning* panel in the API module's project *Properties* dialog.)
_Always prefer friend APIs to implementation dependencies where there is a choice._
==== Implementation dependencies, Auto Update, and <verifyupdatecenter>
Implementation dependencies cause special problems for Auto Update. (Some background information is available in link:http://openide.netbeans.org/versioning-policy.html#3[NetBeans API &amp;amp; Module Versioning Policy / Numbering Scheme for Updates].)
The problem is that when an implementation version of a module published to an update server changes, any modules declaring implementation dependencies on it must also be published, with dependencies on the new version of the base module. Furthermore, the Auto Update client has just one method for deciding whether an NBM on a server is an "update" relative to what you already have installed: if its specification version is larger. So consider the following snapshot of an update center. (The syntax is not what the actual XML file looks like, just an abbreviated version that shows parts relevant to this example.)
[source,java]
----
[Monday]
OpenIDE-Module: infrastructure
OpenIDE-Module-Specification-Version: 1.0
OpenIDE-Module-Implementation-Version: 070120
OpenIDE-Module: guifeature
OpenIDE-Module-Specification-Version: 1.0
OpenIDE-Module-Implementation-Version: 070120
OpenIDE-Module-Module-Dependencies: infrastructure = 070120
----
These two modules were built at the same time and could be installed together into a NetBeans instance. So far so good.
Now consider what happens when the developer of `guifeature` adds a major new feature and decides to publish a new version, `1.1`. The next day's build produces
[source,java]
----
[Tuesday]
OpenIDE-Module: infrastructure
OpenIDE-Module-Specification-Version: 1.0
OpenIDE-Module-Implementation-Version: 070121
OpenIDE-Module: guifeature
OpenIDE-Module-Specification-Version: 1.1
OpenIDE-Module-Implementation-Version: 070121
OpenIDE-Module-Module-Dependencies: infrastructure = 070121
----
Again, these two modules could be installed together.
But what if a user connected to the update center on Monday and downloaded both modules, and then connects again on Tuesday looking for updates? `infrastructure` is still listed as `1.0` so Auto Update ignores it (`1.0` is "already installed", after all). `guifeature 1.1` is however a possible update. What if you install this update? The module system will refuse to enable `guifeature` because it requests `infrastructure = 070121`, whereas you have `infrastructure = 070120`. Oops!
The solution (short of not using implementation dependencies at all) is to use the NetBeans build harness to compute a specification version. The developer removes `OpenIDE-Module-Specification-Version` from `manifest.mf` in the source projects for _both_ modules. `manifest.mf` for `infrastructure` instead will get
[source,java]
----
OpenIDE-Module-Implementation-Version: 1
----
(only positive integers 1, 2, ... are supported!). And `nbproject/project.properties` for both modules will get the specification version in a new form:
[source,java]
----
spec.version.base=1.0.0
----
The IDE's GUI for module projects lets you do all this without editing metadata files manually; just click the option *Append Implementation Versions Automatically* in the *Versioning* panel of the *Properties* dialog.
(The extra `.0` is required for modules in the NetBeans distribution. When sources are branched for a release, `spec.version.base` is incremented to `1.0.1`, `1.0.2`, ... for each release on the branch. "Trunk" (development) changes increment the first or second digits, e.g. `1.1.0`, `1.2.0`, ...)
The effect of using `spec.version.base` is that our AU snapshots now look like this instead:
[source,java]
----
[Monday]
OpenIDE-Module: infrastructure
OpenIDE-Module-Specification-Version: 1.0.0.1
OpenIDE-Module-Build-Version: 070120
OpenIDE-Module-Implementation-Version: 1
OpenIDE-Module: guifeature
OpenIDE-Module-Specification-Version: 1.0.0.1
OpenIDE-Module-Implementation-Version: 070120
OpenIDE-Module-Module-Dependencies: infrastructure = 1
[Tuesday]
OpenIDE-Module: infrastructure
OpenIDE-Module-Specification-Version: 1.0.0.1
OpenIDE-Module-Build-Version: 070121
OpenIDE-Module-Implementation-Version: 1
OpenIDE-Module: guifeature
OpenIDE-Module-Specification-Version: 1.1.0.1
OpenIDE-Module-Implementation-Version: 070121
OpenIDE-Module-Module-Dependencies: infrastructure = 1
----
The update to `guifeature` is now safe; it can still use `infrastructure` from Monday. Note the new "build version" tag which is used only for diagnostics, not for dependencies.
If there is actually a change in the signature of anything in `infrastructure` that might affect `guifeature`, then the developer merely needs to increment the implementation version in `infrastructure/manifest.mf`:
[source,java]
----
[Wednesday]
OpenIDE-Module: infrastructure
OpenIDE-Module-Specification-Version: 1.0.0.2
OpenIDE-Module-Build-Version: 070122
OpenIDE-Module-Implementation-Version: 2
OpenIDE-Module: guifeature
OpenIDE-Module-Specification-Version: 1.1.0.2
OpenIDE-Module-Implementation-Version: 070122
OpenIDE-Module-Module-Dependencies: infrastructure = 2
----
If the user connects to the update center on Wednesday, the wizard will display both modules as needing to be updated - which is exactly what you want.
How is this system enforced? For one thing, attempts to use inherently unsafe implementation dependencies, or incorrect uses of `spec.version.base`, should produce warnings during the module build process. So look at the output of Ant once in a while and see if the build harness is telling you something.
There is also a continuous builder at link:http://deadlock.netbeans.org/hudson/job/nbms-and-javadoc/[http://deadlock.netbeans.org/hudson/job/nbms-and-javadoc/] which (among other things) tries to build NBMs for all modules in the NetBeans standard distribution plus those experimental "alpha" modules normally published on the update center for development builds. If you commit changes to experimental modules this build will be triggered; failures are mailed to `broken_builds@netbeans.org`, which all developers of modules in netbeans.org ought to subscribe to.
This builder uses an Ant task `<verifyupdatecenter>` to detect dependency problems among NBMs. There are two checks:
1. Can the NBMs just built all be enabled together? (_synchronic consistency_)
2. Suppose I had connected to the update center produced by the previous successful build and installed everything, and now I connected again to this build's update center and asked for all updates. Would any updated modules be broken, due to dependencies on new versions of other modules which were not updated? (_diachronic consistency_)
The second check is what will catch a lot of mistakes in usage of implementation dependencies as described above. Unfortunately it is not feasible to run the second check as part of an offline build process in your own source checkout, as it depends on a build of older sources; so you will need to commit changes and wait for the next build to verify them.
Generally there are two possible solutions to a
link:http://deadlock.netbeans.org/hudson/job/nbms-and-javadoc/lastSuccessfulBuild/testReport/org.netbeans.nbbuild/VerifyUpdateCenter/[test failure]
from this stage:
1. Remove the implementation dependencies; switch to friend dependencies or public APIs.
2. Ensure that all implementation dependencies are against positive integers (not dates), and that `spec.version.base` is used on _both_ sides of the dependency, as described above.
In either case, to fix a test failure
you will generally also need to increment the specification versions
of modules on _both_ sides of the dependency.
<hr/>
Applies to: NetBeans 5.x, 6.x
Platforms: all
=== Apache Migration Information
The content in this page was kindly donated by Oracle Corp. to the
Apache Software Foundation.
This page was exported from link:http://wiki.netbeans.org/DevFaqImplementationDependency[http://wiki.netbeans.org/DevFaqImplementationDependency] ,
that was last modified by NetBeans user Jglick
on 2011-08-03T14:59:11Z.
*NOTE:* This document was automatically converted to the AsciiDoc format on 2018-02-07, and needs to be reviewed.