Manage addon dependencies

This section explains how to include runtime and build time dependencies into ESF addons.

In a OSGi framework Java packages are usually exported by the providing plugin via manifest declaration. As an example, see asset provider MANIFEST.MF.

At runtime, bundles can hide packages and classes from other bundles, as well as share packages with other bundles via the Import-Package and Export-Package manifest directives. See OSGi specification Import-Package and OSGi class loading architecture for further reference.

When building an ESF addon the compiler needs to resolve these dependencies at build-time. In a build based on Tycho, this is usually achieved by specifying a target-platform that contains all the required plugins that export the needed dependencies. A default target platform containing a minimal ESF runtime environment is provided when using the ESF addon archetype, but in some cases this might not be sufficient.

The following scenarios are possible:

  1. dependency is an OSGi bundle already provided by the target platform of the framework;
  2. dependency is an OSGi bundle and is provided by an addon that is not present in the target platform;
  3. dependency is not provided in any target platform nor any external addon.

Case #1 is trivial: you should be able able to compile your addon using the provided dependencies without any modification.

Case #2 requires the addon providing the dependency to be included in the current target platform.

Case #3 requires the artifact providing the dependency to be included into the bundle's classpath at runtime. In case the dependency is a OSGi bundle, it can be included into the set of installed features modifying the features project provided by the ESF addons archetype.

Include dependency in target platform

Create a Maven project called target-platform with a pom.xml that performs the following operations.

Download dependency into a well known path

Usually done in the target folder since it is ignored by git so that the dependency is not committed to the repository.

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-dependency-plugin</artifactId>
  <version>3.1.1</version>
  <executions>
    <execution>
      <id>copy-bundles-for-publishing</id>
      <phase>generate-resources</phase>
      <goals>
        <goal>copy</goal>
      </goals>
      <configuration>
        <stripVersion>true</stripVersion>
        <outputDirectory>${project.basedir}/target/source/plugins</outputDirectory>
        <artifactItems>
          <artifactItem>
            <groupId>com.eurotech.framework.j1939</groupId>
            <artifactId>com.eurotech.framework.j1939.core</artifactId>
            <version>${com.eurotech.framework.j1939.core.version}</version>
          </artifactItem>
        </artifactItems>
      </configuration>
    </execution>
  </executions>
</plugin>

Rename downloaded artifacts and create a p2-repo folder in target definition

The p2-repo folder will be a Maven repository with a p2 layout. For further reference, see Eclipse Provisioning Platform (p2). It is a convention to put all target platform dependencies into a project called target-definition, which is already included in the ESF addon archetype.

<plugin>
  <!--  rename bundles with version appended -->
  <artifactId>maven-antrun-plugin</artifactId>
  <version>1.8</version>
  <executions>
    <execution>
      <id>rename-with-version</id>
      <phase>process-resources</phase>
      <goals>
        <goal>run</goal>
      </goals>
      <configuration>
        <target>
          <move
                file="target/source/plugins/com.eurotech.framework.j1939.core.jar"
                tofile="target/source/plugins/com.eurotech.framework.j1939.core_${com.eurotech.framework.j1939.core.version}.jar" />
        </target>
      </configuration>
    </execution>
    <execution>
      <id>install-target-platform</id>
      <phase>install</phase>
      <goals>
        <goal>run</goal>
      </goals>
      <configuration>
        <tasks>
          <delete dir="../target-definition/p2-repo/" />
          <mkdir dir="../target-definition/p2-repo/repository" />
          <copy todir="../target-definition/p2-repo/repository">
            <fileset dir="${project.build.directory}/repository" />
          </copy>
          <copy todir="../target-definition/p2-repo/source">
            <fileset dir="${project.build.directory}/source" />
          </copy>
        </tasks>
      </configuration>
    </execution>
  </executions>
</plugin>

Use Tycho plugin to generate the p2 repo

The following Tycho plugin is needed to generate the final p2 repository that can be consumed by the tycho-compiler plugin.

<plugin>
  <groupId>org.eclipse.tycho.extras</groupId>
  <artifactId>tycho-p2-extras-plugin</artifactId>
  <version>1.7.0</version>
  <executions>
    <execution>
      <phase>prepare-package</phase>
      <goals>
        <goal>publish-features-and-bundles</goal>
      </goals>
    </execution>
  </executions>
  <configuration>
    <compress>false</compress>
  </configuration>
</plugin>

Consume the p2 repository

Add the generated p2 repository to the repositories list in the pom.xml where the addon is compiler (tycho-compiler is called).

<repository>
  <id>kura_eth-p2</id>
  <layout>p2</layout>
  <url>file://${project.basedir}/../../target-definition/p2-repo/repository</url>
</repository>

After these operations, we need the Eclipse IDE to be made aware of the new dependencies. This can be done by using the IDE user interface. Open with Eclipse IDE the .target file included in the target-definition project and import the newly created p2 repository as a directory (see picture below).

📘

Compile the project

The target platform needs to be compiled separately, before the addon itself. Keep that in mind when automating the CI pipelines.

mvn -f target-platform/pom.xml clean install
mvn clean install

Include dependency into classpath

Usually, non OSGi JARs (usually artifacts downloaded from maven central) cannot be included in the target platform since they do not have the OSGi manifest declarations. The way to proceed in this case it to download the artifact and include it in the classpath and the final JAR of the ESF addon.

To achieve this, the maven build should download the artifact into a well-known location:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-dependency-plugin</artifactId>
  <executions>
    <execution>
      <id>copy-dependencies</id>
      <goals>
        <goal>copy</goal>
      </goals>
      <configuration>
        <artifactItems>
          <artifactItem>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>42.7.3</version>
            <type>jar</type>
            <overWrite>true</overWrite>
            <outputDirectory>lib/</outputDirectory>
            <destFileName>org.postgresql.jar</destFileName>
          </artifactItem>
      </configuration>
    </execution>
  </executions>
</plugin>

Then include the downloaded JAR into the bundle's classpath by modifying the MANIFEST.MF as:

Bundle-ClassPath: .,
 lib/org.postgresql.jar

Finally, package the downloaded JAR into the output binaries by modifying the build.properties file as:

bin.includes = .,\
               META-INF/,\
               OSGI-INF/,\
               lib/org.postgresql.jar

The disadvantage of this approach is that the build output JAR will be bigger in size since it includes all the dependencies artifacts.