SKM IT World

Just another blog about IT


1 Comment

Generate P2 Repository From Maven Artifacts In 2017

Some years ago, I wrote a blog post about how to generate a P2 repository based on Maven artifacts. That described approach is obsolete nowadays and I’d like to show a new approach that is based on the p2-maven-plugin that was created to solve exactly this problem.

P2-Maven-Plugin Integration in Maven Build Life Cycle

First at all, we bind the p2-maven-plugin’s goal site to the Maven’s life cycle phase package. This goal is responsible for the generation of the P2 repository.

<plugin>
  <groupId>org.reficio</groupId>
  <artifactId>p2-maven-plugin</artifactId>
  <version>1.3.0</version>
  <executions>
    <execution>
      <id>default-cli</id>
      <phase>package</phase>
      <goals>
        <goal>site</goal>
      </goals>
      <!--... -->
    </execution>
  </executions>
</plugin>

Generating P2 Repository

Now, we can define which Maven artifacts should be a part of the new P2 repository. It is irrelevant for the p2-maven-pluging if the defined artifacts have already a OSGi manifest or not. If no OSGi manifest exists, the plugin will generate one.


<execution>
<!-- ... -->
<configuration>
  <artifacts>
    <!-- specify your dependencies here -->
    <!-- groupId:artifactId:version -->
    <artifact>
      <id>com.google.guava:guava:jar:23.0</id>
      <!-- Artifact with existing OSGi-Manifest-->
    </artifact>
    <artifact>
      <id>commons-io:commons-io:1.3</id>
      <!-- Artifact without existing OSGi-Manifest-->
    </artifact>
  </artifacts>
</configuration>
</execution>

The artifacts are specified by the pattern groupId:artifactId:version. If you want to save some typing, use the Buildr tab on MVN repository website for copying the right dependency declaration format.

This sample configuration creates a P2 repository that look like the following one:


target/repository
├── artifacts.jar
├── category.xml
├── content.jar
└── plugins
    ├── com.google.code.findbugs.jsr305_1.3.9.jar
    ├── com.google.errorprone.error_prone_annotations_2.0.18.jar
    ├── com.google.guava_23.0.0.jar
    ├── com.google.j2objc.annotations_1.1.0.jar
    ├── commons-io_1.3.0.jar
    └── org.codehaus.mojo.animal-sniffer-annotations_1.14.0.jar

1 directory, 9 files

 

The default behavior of the plugin is, that all transitive dependencies of the defined artifact are also downloaded and packed into the P2 repository. If you don’t want it, then you have to set the option transitive to false in the corresponded artifact declaration. If you need the sources (if they exist in the Maven repository) of the defined artifact in the P2 repository, then you have to set the option source to true in the corresponded artifact declaration.

<!-- ... -->
<artifact>
  <id>com.google.guava:guava:jar:23.0</id>
  <transitive>false</transitive>
  <source>true</source>
</artifact>
<!-- ... -->

Then the generated P2 repository looks like the following one:


target/repository
├── artifacts.jar
├── category.xml
├── content.jar
└── plugins
    ├── com.google.guava.source_23.0.0.jar
    ├── com.google.guava_23.0.0.jar
    └── commons-io_1.3.0.jar

1 directory, 6 files

Generating P2 Repository With Grouped Artifacts

In some situations, you want to group artifacts in so-called feature. p2-maven-plugin provides an option that allows to group the Maven artifact directly into features. The definition of the artifacts is the same like above. The difference is that it has to be inside the corresponded feature. Then, the feature definition needs some meta data information like feature ID, feature version, description etc.


<!-- ...-->
<configuration>
  <featureDefinitions>
    <feature>
      <!-- Generate a feature including artifacts that are listed below inside the feature element-->
      <id>spring.feature</id>
      <version>4.3.11</version>
      <label>Spring Framework 4.3.11 Feature</label>
      <providerName>A provider</providerName>
      <description>${project.description}</description>
      <copyright>A copyright</copyright>
      <license>A licence</license>
      <artifacts>
        <artifact>
          <id>org.springframework:spring-core:jar:4.3.11.RELEASE</id>id>
        </artifact>
        <artifact>
          <id>org.springframework:spring-context:jar:4.3.11.RELEASE</id>id>
          <source>true</source>
        </artifact>
      </artifacts>
    </feature>
    <!--...-->
  </featureDefinitions>
  <!-- ... -->
<configuration>

Then the generated P2 repository looks like the following one:


target/repository
├── artifacts.jar
├── category.xml
├── content.jar
├── features
│   └── spring.feature_4.3.11.jar
└── plugins
    ├── org.apache.commons.logging_1.2.0.jar
    ├── org.springframework.spring-aop.source_4.3.11.RELEASE.jar
    ├── org.springframework.spring-aop_4.3.11.RELEASE.jar
    ├── org.springframework.spring-beans.source_4.3.11.RELEASE.jar
    ├── org.springframework.spring-beans_4.3.11.RELEASE.jar
    ├── org.springframework.spring-context.source_4.3.11.RELEASE.jar
    ├── org.springframework.spring-context_4.3.11.RELEASE.jar
    ├── org.springframework.spring-core_4.3.11.RELEASE.jar
    ├── org.springframework.spring-expression.source_4.3.11.RELEASE.jar
    └── org.springframework.spring-expression_4.3.11.RELEASE.jar

2 directories, 14 files

Of course both options (generating p2 repository with feature and only with plugins) can be mixed.

p2-maven-plugin provides more options like excluding specific transitive dependencies, referencing to other eclipse features and so on. For more information, please look at the p2-maven-plugin homepage.

Now, we can generate P2 repositories from Maven artifacts. We lacks of how to deploy this P2 repository to a Repository manager like Artifactory or Sonatype Nexus. Both repository manager supports P2 repositories, Artifactory in the Professional variant (cost money) and Sonatype Nexus in OSS variant (free). For Nexus, it’s important that you use the version 2.x. The newest version, 3.x, doesn’t yet support P2 repositories.

Deploying P2 Repository to a Repository Manager

First at all, we want that our generated P2 repository is packed into a zip file. Therefore, we add the tycho-p2-repository-plugin to the Maven build life cycle:


<plugin>
  <groupId>org.eclipse.tycho</groupId>
  <artifactId>tycho-p2-repository-plugin</artifactId>
  <version>1.0.0</version>
  <executions>
    <execution>
      <phase>package</phase>
      <goals>
        <goal>archive-repository</goal>
      </goals>
    </execution>
  </executions>
</plugin>


Then, we have to mark this zip file, so that Maven recognize that it has to deploy it during the deploy phase to a repository manager. For this, we add the build-helper-maven-plugin to the Maven build life cycle.

<!-- Attach zipped P2 repository to be installed and deployed in the Maven repository during the deploy phase. -->
<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>build-helper-maven-plugin</artifactId>
  <version>3.0.0</version>
  <executions>
    <execution>
      <goals>
        <goal>attach-artifact</goal>
      </goals>
      <configuration>
        <artifacts>
          <artifact>
            <file>target/${project.artifactId}-${project.version}.zip</file>
            <type>zip</type>
          </artifact>
        </artifacts>
      </configuration>
    </execution>
  </executions>
</plugin>

Now, the generated P2 repository can be addressed by other projects. For more information about how to address the P2 repository, please have a look on the documentation of your repository manager.

A whole pom.xml sample can be found on Github.

Links

Advertisements


Leave a comment

Launching Several Pax Runner Daemons With The Same User

I use Pax Runner to provisioning my OSGi applications. I want to use several OSGi applications on my test server. As soon as you start the second OSGi application provisioned by Pax Runner, you get the following exception:


Exception in thread &quot;main&quot; java.lang.RuntimeException: org.ops4j.pax.runner.daemon.lock exists. Please make sure that the Pax Runner daemon is not already running.

As per Google search it is a bug in Pax Runner. The source code of  Pax Runner is hosted by Github, so I create a fork for fixing this issue.

My first idea was to add a new runner option. Problem is that the method, which build the path for the daemon home directory, is static, so I have no chance to read the runner option without a big refactoring. I decided to add a new Java property relative.runner.home., so I need to change only one method getRunnerHomeDir. Now, the path of the daemon runner home directory is user.home/.pax/relative.runner.home. If this property is not set, the default path will be  user.home/.pax/runner (like the behaviour before this change).

With this change, the start call of the Pax Runner looks like that:


java -Drelative.runner.home=app1 -cp pax-runner-1.7.6-SKM-PATCH.jar org.ops4j.pax.runner.daemon.DaemonLauncher --start

and the stop call of the Pax Runner looks like that:


java -Drelative.runner.home=app1 -cp pax-runner-1.7.6-SKM-PATCH.jar org.ops4j.pax.runner.daemon.DaemonLauncher --stop

For the second OSGi application, you have to change the value of relative.runner.home. But the second application will not start because of the following exception:


Exception in thread &quot;main&quot; java.lang.RuntimeException: Unable to set up shutdown port [8008].
at org.ops4j.pax.runner.daemon.Daemon.await(Daemon.java:253)
at org.ops4j.pax.runner.daemon.Daemon.start(Daemon.java:134)
at org.ops4j.pax.runner.daemon.Daemon.main(Daemon.java:84)
at org.ops4j.pax.runner.daemon.DaemonLauncher.start(DaemonLauncher.java:91)
at org.ops4j.pax.runner.daemon.DaemonLauncher.main(DaemonLauncher.java:69)
Caused by: java.net.BindException: Address already in use: JVM_Bind
at java.net.TwoStacksPlainSocketImpl.socketBind(Native Method)
at java.net.AbstractPlainSocketImpl.bind(AbstractPlainSocketImpl.java:376)
at java.net.TwoStacksPlainSocketImpl.bind(TwoStacksPlainSocketImpl.java:101)
at java.net.PlainSocketImpl.bind(PlainSocketImpl.java:175)
at java.net.ServerSocket.bind(ServerSocket.java:376)
at java.net.ServerSocket.&lt;init&gt;(ServerSocket.java:237)
at java.net.ServerSocket.&lt;init&gt;(ServerSocket.java:128)
at org.ops4j.pax.runner.daemon.Daemon.await(Daemon.java:250)
... 4 more
-&gt; Pax Runner daemon stopped.

Pax Runner uses a shutdown port to close the application, so every application needs its own shutdown port. You have two possibilities to set the shutdown port.

  1. You create a file called runner.args the same directory where the pax runner jar is and add the option –org.ops4j.pax.runner.daemon.shutdown.port=8008 in this file.
  2. You get this option to the DaemonLauncher, directly:
    java -Drelative.runner.home=app2 -cp pax-runner-1.7.6-SKM-PATCH.jar org.ops4j.pax.runner.daemon.DaemonLauncher --start --org.ops4j.pax.runner.daemon.shutdown.port=8008
    

Links

  1. Pax Runner Homepage
  2. Jira Bug Ticket
  3. Pax Runner at Github
  4. Pax Runner Fork


Leave a comment

Reading tips for OSGi Beginner

In the last weeks I familiarised myself with OSGi. Two books were very helpful:

Modular Java: Creating Flexible Applications with OSGi and Spring by Craig Walls

OSGi für Praktiker – Prinzipien, Werkzeuge und praktische Anleitungen auf dem Weg zur kleinen SOA by Bernd Weber,  Patrick Baumgartner, Oliver Braun (in German)

Both books are similar organised.  They introduce OSGi by an example project form the first source code till the deployment.  These projects are not ‘Hello World’ projects, so you can adopt the way of working with OSGi for your real OSGi projects.

After the books show the classic way to coding with OSGi, they show how you can use dependency injection in OSGi applications with Spring DM.

Both books work with the PAX Tooling of OPS4J. They are Maven Plugins and track the ‘pom-first’ way to build OSGi applications.  These books have a good introduction to the PAX tooling, too. It is very helpful, when you want to use a continuous integration environment for your development (Of course, you want 🙂 ).

The book ‘Modular Java’ has one minor flaw. It was written in 2009, so it use elder version of the tools.