Best way to convert existing java projects to osgi bundles - java

We have lot of components out of which we want to modularize only a few to start with. Wondering what is the best way (in the context of my build environment) to create bundles out of all these components?
My environment: Java 6, Maven 2.2.1, Hudson
Technologies: Spring 3.0.5, WebSphere 7, Hibernate 3.2.x and most of apache commons.
Requirements
Modularize only few components. Rest of the components can export all of the packages.
When imported into eclipse, I should be able to see the bundles of imported-packages as dependencies in build path (mvn eclipse:eclipse doesn't seem to do this)

Start by only changing the MANIFEST.MF entries such that all your artifacts become bundles - they obviously won't magically work but it's a good non-destructive first step.
When using the maven-bundle-plugin ensure you set extensions and supportedProjectTypes as you may have problems with CI builds, Maven repos and m2e failing if the packaging type is bundle (see end).
Test your riskiest/core external dependencies early on - for example if you're using JPA for persistence then ensure that the provider works in an OSGi environment with your domain bundle and JDBC driver.
If you're migrating from Java EE/spring look at Karaf or Virgo. But if your components are for embedded systems or have no external dependencies the Felix or Equinox may be enough (though check out the pax-url project if that's the case).
Might be worth editing your question to be a bit more specific about the domain/technologies?
eclipse:eclipse only generates that when the project is first configured, m2e's lifecycle problems might be a bit of pain but it's far better than using the old eclipse plug.
The following will add manifest entries to your existing artifacts without changing them in any other way. It tells the standard maven jar and war plugins to use the MANIFEST.MF generated by maven-bundle-plugin.
Put this in the parent POM:
<pluginManagement>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>2.3.7</version>
<extensions>true</extensions>
<configuration>
<archive>
<addMavenDescriptor>true</addMavenDescriptor>
</archive>
<supportedProjectTypes>
<supportedProjectType>jar</supportedProjectType>
<supportedProjectType>war</supportedProjectType>
</supportedProjectTypes>
<instructions>
<Built-By>${project.organization.name}</Built-By>
<Bundle-Vendor>${project.organization.name}</Bundle-Vendor>
<Bundle-ContactAddress>${project.organization.url}</Bundle-ContactAddress>
<Bundle-Description>${project.description}</Bundle-Description>
<Bundle-DocURL>${bundle.doc.url}</Bundle-DocURL>
<Bundle-Category>${bundle.category}</Bundle-Category>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
<Bundle-Version>${project.version}</Bundle-Version>
<Import-Package>*</Import-Package>
<Export-Package>*</Export-Package>
</instructions>
</configuration>
<executions>
<execution>
<id>bundle</id>
<goals>
<goal>manifest</goal>
</goals>
<phase>prepare-package</phase>
<inherited>true</inherited>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.1</version>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.1.1</version>
<executions>
<execution>
<id>create-war</id>
<phase>package</phase>
<goals>
<goal>war</goal>
</goals>
<inherited>true</inherited>
</execution>
</executions>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
</pluginManagement>
Then in child POMs, you may simply do:
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
</plugin>
<!-- Below is mutually exclusive: Either jar or war plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
</plugin>
</plugins>
</build>

Take a look at bndtools, it has excellent support for creating projects that wrap bundles. It provides a lot of insight how JARs are structured and how they depend on other things.

Use the maven bundle plugin. It will add the required import and export statements to you manifest based on scanning your code and the dependencies defined in the pom. This will require the least amount of effort to convert.
I also recommend you use M2Eclipse instead of mvn eclipse:eclipse. It will keep your maven config and workspace in sync.

Related

SpringBoot fully executable jar without dependencies inside

NOTE: Please, before marking this question as a duplicate make sure you know the difference between executable JAR and fully executable SpringBoot JAR.
The official Spring Boot documentation describes how to build fully executable JAR. Then generated JAR file can be linked from /etc/init.d/ and started/stopped/restarted/statused as a normal unix service without additional scripts or tools like JSVC.
But the generated JAR contains all libraries and can be big enough in size (in my case 70Mb+).
I want to generate such fully executable JAR without libraries, but then to be able to run it as SystemV service on Linux and link external libraries (JARs) somehow.
UPDATE
I want to reduce the artifact size in order to speed up deploy->test->fix cycle. Sometimes I'm working via mobile network and big file size can decrease my job speed dramatically.
In case there is no a simple configuration property or a profile or a command line option I would use a kind of hack.
At the beginning, I can generate a build containing all dependencies.
Then I can unzip it and move all libraries to a special folder.
Then I need to pack it again as fully executable somehow and run with pointing to the folder with libraries.
I don't think this can be done with jar utility because file utility recognizes fully executable jar as data
$ file fully-executable.jar
file fully-executable: data
unlike the usual jar
$ file usual.jar
usual.jar: Java Jar file data (zip)
You may want to consider using Spring Boot Thin Launcher. It creates a jar file with your application code but none of its dependencies. It adds a special thin launcher that knows how to resolve your application's dependences from a remote Maven repository or from a local cache when the jar is executed. Judging by the description of what you want to do, you'd utilise the local cache option.
The configuration of Spring Boot's Maven plugin to produce a fully executable jar that uses the thin launcher looks like this:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot.experimental</groupId>
<artifactId>spring-boot-thin-layout</artifactId>
<version>1.0.3.RELEASE</version>
</dependency>
</dependencies>
<configuration>
<executable>true</executable>
</configuration>
</plugin>
</plugins>
</build>
I can use this setting to create spring boot jar without dependency jars.
Copy dependency jars to dist/lib
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<includeScope>runtime</includeScope>
<outputDirectory>${project.build.directory}/dist/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
Specify 'lib' as classpath prefix, so MAINFEST.MF will be created like this:
Class-Path:lib/httpcore-nio-4.4.14.jar lib/guava-24.1.1-jre.jar...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>com.xxx.MyMainClass</mainClass>
</manifest>
</archive>
<outputDirectory>${project.build.directory}/dist</outputDirectory>
</configuration>
</plugin>
Let spring boot plugin includes dependency which is not existing, will cause spring boot jar exclude all dependencies.
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<layout>ZIP</layout>
<includes>
<include>
<groupId>not-exist-in-my-project</groupId>
<artifactId>not-exist-in-my-project</artifactId>
</include>
</includes>
<outputDirectory>${project.build.directory}/dist</outputDirectory>
</configuration>
</plugin>

How to generate an OSGI module with maven?

Is it possible to generate a OSGI Module with maven?
Usually I can generate a project like
mvn -B archetype:generate \
-DarchetypeGroupId=org.apache.maven.archetypes \
-DgroupId=com.my.company \
-DartifactId=hello-world
Is there a similar way to get an OSGI module?
Not sure. Probably the archetypes are a bit outdated. I simply create a normal maven project and add the bnd-maven-plugin at the parent (see here for an example).
Alternatively you can use the maven-bundle-plugin. See here.
<plugin>
<groupId>biz.aQute.bnd</groupId>
<artifactId>bnd-maven-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<goals>
<goal>bnd-process</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.5</version>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
With this present all your modules will be bundles. If you need to tune the Manifest settings then you create a bnd.bnd file in your module. You can edit this by hand or with the bndtools bnd file editor.

Maven package effective pom

I have a Maven project with a number of sub modules. Some of these sub modules are packaged as jar that are deployed to a Nexus Maven repository.
The problem I have is that the packaged jar references the parent pom which is not necessarily deployed.
Is there a way for Maven to deploy the effective pom instead of the pom.xml?
You need to be perfectly aware of the consequences of what you want to do: the effective POM will also contain your current settings (content of settings.xml), thereby possibly publicly exposing whatever passwords you have hard-coded in there. A better solution would be just to deploy the parent POM.
However, if you really want to go down that path, you can have the following configuration:
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<addMavenDescriptor>false</addMavenDescriptor>
</archive>
</configuration>
</plugin>
<plugin>
<artifactId>maven-help-plugin</artifactId>
<version>2.1.1</version>
<executions>
<execution>
<phase>generate-resources</phase>
<goals>
<goal>effective-pom</goal>
</goals>
<configuration>
<output>${project.build.outputDirectory}/META-INF/maven/${project.groupId}/${project.artifactId}/pom.xml</output>
</configuration>
</execution>
</executions>
</plugin>
This tells the maven-jar-plugin not to add the Maven descriptor pom.xml and pom.properties to the jar. Instead, the pom.xml is generated by the maven-help-plugin and its effective-pom goal.
If you want the pom.properties file also, you will need to create it manually with the maven-antrun-plugin.

Maven deploy jar with dependencies to repo

I can deploy a jar by using the following in my pom.xml and running mvn deploy:
<distributionManagement>
<repository>
<id>releases</id>
<url>http://${host}:8081/nexus/content/repositories/releases</url>
</repository>
<snapshotRepository>
<id>snapshots</id>
<name>Internal Snapshots</name>
<url>http://${host}:8081/nexus/content/repositories/snapshots</url>
</snapshotRepository>
</distributionManagement>
And I can build an executable jar-with-dependencies using the following:
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>create-executable-jar</id>
<phase>deploy</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>my.company.app.Main</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
Problem is I don't know how to stitch these together to deploy the executable jar to my Maven repo. I don't really know if this is accomplished by a new plugin or by adding a goal or other step to the existing assembly plugin.
If you bind the assembly to the packaging phase, it will install in your repository both the "regular" jar and the with-dependencies jar when you do a build:
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>my.company.app.Main</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id> <!-- this is used for inheritance merges -->
<phase>package</phase> <!-- bind to the packaging phase -->
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
Then simply run mvn clean install deploy to upload both jars to your repository.
In order to build a (so-called) Über JAR and deploy it using maven, you could also use the shade plugin. The following code is taken from their website but I've made one or two projects using this feature.
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<shadedArtifactAttached>true</shadedArtifactAttached>
<shadedClassifierName>jackofall</shadedClassifierName> <!-- Any name that makes sense -->
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>
In this configuration you get the Über JAR as one deployment besides the normal JAR. The user of your JAR can then decide to pull the all-in-one package or the JAR with dependencies based on the classifier.
I'll usually use the shade plugin to build Über JARs (or modify the JAR in a way) and use the assembly plugin to build things like installation packages (containing the JAR and possibly other things). I am unsure what the intended goals of the single plugins are however.
The following worked. I'm going to leave this question open a bit because I'm not positive this is best practice, but working is something.
Problems I notice are that I made up the ID name and I don't know if this is usual practice and that I have to hard code the jar name; it isn't inferred from anything else.
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
<executions>
<execution>
<id>deploy-executable</id>
<goals>
<goal>deploy-file</goal>
</goals>
<configuration>
<file>target/Monitoring-Client-1.0-SNAPSHOT-jar-with-dependencies.jar</file>
</configuration>
</execution>
</executions>
</plugin>
Essentially my difficulty doing this revealed the fact that my pom.xml was way off the rails already. Everything would have snapped into place on its own. I was formerly doing:
Save all the dependencies into a lib folder
Build a jar with a classpath slurping up that lib folder
Use the assembly plugin to make another deployable jar
I think there were several reasons this sort of made sense along the way, especially when my libraries were not well-factored from my applications.
However by deleting 1 and 2 all that is needed is the distributionManagement section and the deploy phase works automagically. So all in all this is an amazing case of literally adding functionality by deleting large swathes of code.
First you shouldn't do the creation of the ueber jar in the deploy phase it's better to do this in the package phase. Furthermore the created jar file is usually automatically attached to your artifact and will be transfered to the remote repository (in your case Nexus). You can check this if you simply try to do a mvn install and take a look at the output if the created jar is installed into the local repository.
To deploy the results into nexus you need to call mvn deploy.

What is the best way to avoid maven-jar?

I am using a different plugin (ant4eclipse) to jar my files. What is the best way to avoid the maven-jar plugin from executing?
I tried to remove the <plugin>maven-jar-plugin</plugin>
I tried to <exclude> ** / * < / exclude>
I tried to <skip>true</skip>
None worked
In Maven 3.0.x (I tried 3.0.2) you can disable maven-jar-plugin by binding the default-jar execution to a nonexistent phase, as #bmargulies suggested. Unfortunately that doesn't work in 2.2.1, but you can prevent it from interfering with your own jar by setting an alternative <finalName> and <classifier> for the default-jar execution; it will still create a jar, but it will be set as a secondary artifact for the project and won't overwrite the one you've created. Here's an example that should work in both Maven 2 and Maven 3:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>test</artifactId>
<version>0.1-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>default-jar</id>
<phase>none</phase>
<configuration>
<finalName>unwanted</finalName>
<classifier>unwanted</classifier>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Once you've disabled maven-jar-plugin, maven-install-plugin may give you trouble too. In Maven 3 it can be disabled the same as maven-jar-plugin: bind default-install to a nonexistent phase. However, in Maven 2 maven-install-plugin requires that the target/classes directory exist, and it will install the dummy jar when there isn't a primary artifact present.
This should do the trick - notice the use of <id>default-jar</id> and <phase/>.
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<id>default-jar</id>
<phase/>
</execution>
</executions>
</plugin>
</plugins>
</build>
In my case, I only wanted to disable the jar plugin because the jar was empty. You can use the skipIfEmpty option in the plugin configuration
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<skipIfEmpty>true</skipIfEmpty>
</configuration>
</plugin>
What happens if you declare this?
<packaging>pom</packaging>
Even if it does what you're looking for, be careful. I'm not sure if there could be negative side effects -- such as other maven projects that depend on your jar not being able to locate it.
Using maven 3.3.9, the following worked for me:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.2</version>
<executions>
<execution>
<id>default-jar</id>
<phase>none</phase>
<configuration>
<finalName>unwanted</finalName>
<classifier>unwanted</classifier>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>3.0.0-M1</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
So in case of the maven-jar-plugin, I bound it to a non-existent phase. For the maven-install-plugin, I used the "skip" configuration parameter. The documentation about it says: "Set this to true to bypass artifact installation. Use this for artifacts that does not need to be installed in the local repository."
Explicitly bind the jar plugin to a phase that doesn't exist.
As other's have said, it's not possible to turn it off, other than using <packaging>pom</packaging>, which turns everything off and is probably not what you want.
Even though it will generate twice, a working solution is to bind your jar process to the package phase, as that is guaranteed to run after the default. By overwriting the same JAR file, you'll find that yours is used wherever the original would have been.
I am using a different plugin to jar my files. What is the best way to avoid the maven-jar plugin from executing?
First, the jar:jar goal is bound by default on the package phase for a project with a packaging of type jar. Second, there is no way to unbind a plugin bound to a phase. So, if you are using another plugin(?), either accept to produce 2 JARs or change the packaging (but I don't think this will work well).
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>

Categories

Resources