Prev Next

API Project

What you will learn in this section

In this section we are going to create an API for a simple expression evaluator. It will teach you how to create an API project, how to name a project, and how to navigate around in the project. We also explain how to version packages.

You should have setup an empty Workspace as explained in the previous section. The text is written assuming that you are in the bndtools perspective.

This section discuss an API project. An API project can be used on the classpath but cannot run inside a framework. In general the API packages are included by a provider bundle.

Creating a Project

We want to make a component that evaluates expressions. Though we could start with the implementation, let’s do it right and first define the contract. The service contract for now could be an interface:

package com.acme.prime.eval.api;
/**
 * A service that evaluates an expression and returns the result
 */
public interface Eval {
	/**
	 * Evaluate an expression and return the result.
	 */
	double eval(String expression) throws Exception;
}

Ok, it does not get a lot easier than this!

Best practice is to give service contracts their own projects so they can easily be shared. So let’s create an API project. OSGi enRoute has special templates for an API projects. It actually deduces that a new project is an API project when the project name ends with .api.

This is not the only name enRoute recognizes. You can actually use the following suffixes:

  • .test – An OSGi test project, tests are run inside a framework.
  • .provider, adapter – An implementation project
  • .api – API only project
  • .application – An application project. This is a project that binds together a set of components and parameterizes them.

A bug in the templates requires the name to consist of at least 3 segments like: a.b.api.

So let’s create the com.acme.prime.eval.api project. Start with New/Bndtools/Bnd OSGi Project.

This will open a dialog page where we are going to select a template. For the template, select the OSGi enRoute templates. The OSGi enRoute template will pick an appropriate layout for your project depending on the last segment or extension of your project name.

Create API Project

Add the name of the project (com.acme.prime.eval.api).

Create API Project

Then you can just click through Next. We do not have to set anything on this page, just continue and click Finish. This creates a small project with all the options set to treat it as an API project. The templates already created the Eval interface for you in the right package, just fill in the source code we discussed earlier.

Add the Interface

That is it! You just created your first bundle! If you do not believe it, then go to the generated folder in the project and double click on the com.acme.prime.eval.api.jar file. This will open a JAR Viewer on your fresh bundle. By default, it opens the Manifest which was generated by bnd but you can traverse the bundle and look at the contents.

Generated bundle

Export

Notice how the manifest shows we’ve exported the com.acme.prime.eval.api package. Exporting means it has been made available to other bundles. You can see the exports (and imports) if you double-click on the bnd.bnd file and select the Contents tab. In the Export Packages list you can see that we do export this package.

Generated bundle

Our API bundle therefore looks as follows:

Generated bundle

The box with rounded corners represents a bundle; the inside black box represents an exported package.

Versioning

You might not have noticed it but you actually semantically versioned this package as well (don’t you love magic?). When you look in the com.acme.prime.eval.api package you will find the package-info.java file. Double clicking will show the contents. In this file we define the version of the package. If you make modifications to the package, you should always update the version in this file as well.

@org.osgi.annotation.versioning.Version("1.0.0")
package com.acme.prime.eval.api;

Provider & Consumer Types

In this API, any party that will implement the Eval interface is considered to be the provider. A provider must fully implement a contract that has virtually no backward compatibility unlike consumers of this API. Any change in the version that affects the public API must result in rebuild of the provider’s bundle. That is, if our version here goes to 1.1 we want to make sure our providers that implemented 1.0 are no longer compatible.

Obviously it is a nightmare to ensure that the proper version ranges are used. We can significantly help the provider by adding an annotation to this interface:

@ProviderType
public interface Eval { ... }

The bnd tool will now automatically ensure that any implementers of this interface use semantic versioning to import the package with a minor range, for example [1.0,1.1).

Interfaces that are implemented by the consumer of your API (usually listener-like interfaces) can be annotated with the @ConsumerType annotation. However, this is the default.

If you find this hard to grasp then you’re not alone. This is a very complex area. We will get back to this later.

Compile Only

If you look in the bnd.bnd file you see that this API bundle is compile only:

Require-Capability: \
	compile-only

The reason this is compile only is that best practices dictate that the provider should export its API. There have been numerous discussions about this and some opinions differ. However, a provider of an API is extremely closely tied to the version of the API it provides. Virtually any change in the API requires a change in the provider, there is no backward compatibility as there is for the API consumers. Therefore, by exporting the API from the provider you make the whole system less complex.

How does it Work?

A bnd project is driven by the bnd.bnd file. This properties file defines the version, the build path and which packages should go into the bundle. In this case the file is:

Bundle-Version:1.0.0.${tstamp}
Bundle-Description: 				\
	This is  project. An API project should in general not contain any \
	implementation code.\
	\
	${warning;Please update this Bundle-Description in com.acme.prime.eval.api/bnd.bnd}

	
Export-Package:  \
	com.acme.prime.eval.api;provide:=true

Require-Capability: \
	compile-only

-buildpath: \
	osgi.enroute.base.api;version=1.0

Bndtools takes this information and instructs Eclipse to act accordingly. In the gradle build we will get to later, bnd instructs gradle environment so that both environments build the same JARs.


Prev Next