The Eclipse plug-in manifest is also known as OSGi Manifest(Although the page says OSGi bundle, some of the described features are specific for Eclipse). In an earlier article I already addressed some of these issues. With this one I want revisit those and describe some more features, that I found helpful in my daily work with plug-in development.
Declaring an exported package internal
When you implement a plug-in you have to ask yourself the question how to split the classes into different packages. which classes can be declared package private. Which classes must be visible from the outside since they define the public API. Some classes must be exported since you either implement some extension point or provide one yourself. When providing one, it is a good idea to provide some basic classes that simplifies any implementation that makes use of the extension point.
But this leaves a gap: If you have to use a class from a different package you must define that class as public. In Eclipse it is good practice to place such classes that are only used by your plug-in in an internal package. The convention for the package name is such: [plug -in ID].internal.[subpackage]. If your whole contribution is contained in a single bundle or plug-in you are free to export only the packages that contain the public API.
In the view of code reuse it makes sense to export all packages that are not to specific (i.e. i18n package). This also means exporting your internal package. Up tho this point the meaning of the internal package is only semantical. Eclipse provides you with the means to enforce this: Append the property
x-internal behind your package declaration. The property can have two values: true or false whereas false is the default, meaning if you don’t declare anything it is equivalent to
Export-Package: org.eclipse.foo.internal.bar; x-internal:=true, org.eclipse.foo.bar
This results in the following behavior: You can use any publicly available class from the internal package but you get a waring (which can be suppressed) that access to that class is discouraged. This means basically: Use at your own risk.
Declaring a hidden package a friend for specific plug-ins
This behavior might be too restrictive, especially when you split your code over different bundles. Here you have a second possibility
x-friend can declare one or more plug-in friends of this plug-in. In effect it is declaring the package internal unless for the listed plug-ins:
Export-Package: org.eclipse.foo.formyfriends; x-friends:="org.eclipse.foo.friend1, org.eclipse.foo.friend2", org.eclipse.foo.bar
You should not use both
x-internal takes precedence over
x-friend, meaning that you would get an access discouraged warning in your friend plug-in.
Don’t activate the plug-in at startup
To increase start up time of your eclipse based application you should whenever possible start the bundle when its first class is loaded. Optionally to the true or false flag you can define a comma separated exclude respectively include list of packages that will respectively will not activate the bundle when a class from the defined package is loaded:
Bundle-ActivationPolicy: lazy; exclude:="org.eclipse.foo1, org.eclipse.foo2"
This activates the bundle when the first class is loaded unless the class resides in one of the defined packages.
Bundle-ActivationPolicy: lazy; include:="org.eclipse.foo1, org.eclipse.foo2"
This activates the bundle at startup unless the class resides in one of the defined packages.
The above is OSGi standard as of R4.1, before that Eclipse (up until 3.3) had its own mechanism that is not described here since it is deprecated but will still be supported. This is especially of interest when you implement plug-ins that must be compatible prior to 3.4.
Class loading concerns
If you have only one plug-in, this issue is of no immediate concern to you. On the other hand if you have split your code over several bundles you should at least be aware of this.
Each bundle has its own classloader. This is also the reason why package private classes in bundle A are not visible in the same package of bundle B at runtime. The plug-ins that are defined in the
Require-Bundle are on the class path. For anything else you must make provisions. This is the case when you need a class from a bundle that you have not defined a dependency. Such classes are commonly invoked by reflexion.
One way to circumvent this restriction are extension points. This is the case when you want to use classes that may not even be written by the time you develop your code.
Another way is to define a buddy policy:
Eclipse-BuddyPolicy ::= ( policy-name ) ( ',' policy-name ) * policy-name ::= ( 'dependent' | 'global' | 'registered' | 'app' | 'ext' | 'boot' | 'parent' )
- dependent – Consults all bundles that directly or indirectly depend on the bundle. An indirect dependency can be introduced by bundles that use Require-Bundle with the visibility directive set to reexport. Note that this casts a rather wide net and may introduce performance problems as the number of bundles increase.
- registered – Consults all dependent bundles that explicitly register themselves as buddies to the bundle. This is similar to the dependent policy but scopes down the number of bundles that will be consulted as buddies.
- global – Consults the available packages exported in the global pool of exported packages.
- app – Consults the application classloader.
- ext – Consults the extension classloader.
- boot – Consults the boot classloader.
The best way is probably to define the registered ploicy and explicitrly add a
Eclipse-RegisterBuddy in the other plug-in. In MANIFEST.MF of org.eclipse.foo:
In MANIFEST.MF of org.eclipse.bar which contains a class called by reflexion:
The context specific class loading is described in more detail on the Eclipse wiki page.
This is a minor issue. Normally when you deploy your plug-in the jar file is put into the plug-ins directory. It may be the case (for whatever reasons) that your plug-in must be unpacked and be represented as a folder with all it’s containing sub folders. To achieve this you can define a bundle shape directive. Two values are possible: jar which is the default and dir:
This directive is only evaluated by the installer, meaning if you deploy your bundle in another way (i.e. drop the bundle manually in the directory) this directive has no effect.