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.

      <!--... -->

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.

<!-- ... -->
    <!-- specify your dependencies here -->
    <!-- groupId:artifactId:version -->
      <!-- Artifact with existing OSGi-Manifest-->
      <!-- Artifact without existing OSGi-Manifest-->

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:

├── artifacts.jar
├── category.xml
├── content.jar
└── plugins
    ├── 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.

<!-- ... -->
<!-- ... -->

Then the generated P2 repository looks like the following one:

├── artifacts.jar
├── category.xml
├── content.jar
└── plugins
    └── 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.

<!-- ...-->
      <!-- Generate a feature including artifacts that are listed below inside the feature element-->
      <label>Spring Framework 4.3.11 Feature</label>
      <providerName>A provider</providerName>
      <copyright>A copyright</copyright>
      <license>A licence</license>
  <!-- ... -->

Then the generated P2 repository looks like the following one:

├── 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:


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. -->

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.



Leave a comment

Pimp My Git – Manage Different Git Identities

I usually work on different Git projects that need different Git identities. My work flow for new repositories was

  1. Clone new repository.
  2. Go to cloned repository.
  3. If it is necessary to change the Git identity, call a shell script that runs `git config “Sandra Parsick”; git config`

I was never happy with this solution, but it works. Fortunately, a tweet of @BenediktRitter and one of @wosc suggest two alternatives to my method.

The first method bases on the Git feature “Conditional Includes” (required Git Version at least 2.13). The idea is that you define a default Git identity and separate Git identities per specific directory. That means, every repository, that is cloned beneath one of the specific directory, has automatically its specified Git identities.

The second method bases on a Python script, that is inspired by the Mercurial extension hg-persona. The idea is that you can individually set a Git identity per Git repository. It is an one command replacement for the git config user.* command serie.

In the next two sections I’d like to summarize how to set up and how to use these two methods. I have tested it on a Debian-based system. Let’s start with the first one.

Summarize Git identity for several Git repositories

As above described, this method bases on the Git feature “Conditional Includes”. Therefore, ensure you have installed your Git client in at least version 2.13 . Assume, we want to have two Git identities, one for Github and one for work. Therefore, create two .gitconfig files in your home folder.

touch ~/.gitconfig_github
touch ~/.gitconfig_work

Then add the specific Git identity in respective .gitconfig files.


   name = YourNameForGithub
   email =


   name = YourNameForWork
   email =

The next step is to add these two .gitconfig files to our global one and to specify when to use them.


   name = defaultName
   email =

[includeIf "gitdir:~/workspace_work/"]
   path = .gitconfig_work

[includeIf "gitdir:~/workspace_github/"]
   path = .gitconfig_github

Now, every repository, that is cloned beneath ~/workspace_work/, has automatically the Git identity for Work (.gitconfig_work) and every repository, that is cloned beneath ~/workspace_github/, has automatically the Git identity for Github (.gitconfig_github). Otherwise, the default Git identity is used.

Setting Git identity individually per Git repository

For the second method, you have to install ws.git-persona from PyPI.

sudo apt-get install pip # if PyPI isn't install
pip install ws.git-persona

Then, open your global ~/.gitconfig and add your personas. In our cases, we add two personas, one for Github and one for work.


  github = YourNameForGithub &lt;;
  work = YourNameForWork &lt;;

In the next step, we want to switch our Git identity in a Git repository. This is now possible with the command git-persona. In the following example we switch to the identity for Github and then to the identity for work.

> git-persona -n github
> git config
> git config
> git-persona -n work
> git config
> git config

If you have other methods to manage different Git identities, let me know it and write a comment.



  1. Blog post about Git feature “Conditional Includes”.
  2. Github repository of git-personas.

1 Comment

Pimp My Git – Git Mergetool

I like to work with git on the command line. But in some cases I prefer UI support. For example, solving merge conflicts is such a case. Git has a command mergetool, which can open a graphical tool to solve merge conflicts. But before you can use this command, you had to configure it. In this blog post I’d like to show you how to configure mergetool and how to use it.


First at all, open a shell on Linux. On Windows, open Git Bash. Then choose a graphic tool that should support you solving merge conflicts. git mergetool –tool-help shows a list which tools are supported on your machine

 sparsick@sparsick-ThinkPad-T430s > git mergetool --tool-help
'git mergetool --tool=<tool>' may be set to one of the following:

The following tools are valid, but not currently available:

Some of the tools listed above only work in a windowed
environment. If run in a terminal-only session, they will fail.

This command shows two lists. The first list shows all tools that are supported by git and that are available on your machine (in sample, it is araxis, kdiff3 and meld). The second list shows that are also supported by git, but they aren’t install on your machine.

I use meld as graphical tool. It’s runnable on Windows and Linux. If you haven’t install meld on your machine, then it’s now the right time to do it or choose another tool.

We want to set mergetool globally for all our repositories.

sparsick@sparsick-ThinkPad-T430s > git config --global merge.tool meld
sparsick@sparsick-ThinkPad-T430s > git mergetool
No files need merging

If git mergetool returns more than No files need merging, then the path to your graphic tool isn’t set in your $PATH system variable (The normal case on Windows systems). It’s possible to set the path to the graphical tool directly in git.

sparsick@sparsick-ThinkPad-T430s > git config --global mergetool.meld.path /c/Program\ Files\ \(x86\)/Meld/Meld.exe</pre>

Bear two important things in mind: mergetool is written without a dot between merge and tool and meld is a placeholder for the name of the graphical tool in the above sample. If you use another tool such like vimdiff, then the config key is called mergetool.vimdiff.path .

Now git mergetool is ready to use.


Now I’d like to demonstrate how to use git mergetool. It is used in when we have merge conflicts during a merge action. Let’s say we want to merge branch branch1 into master and this merge will have some merge conflicts.

sparsick@sparsick-ThinkPad-T430s > git merge branch1

Auto-merging test
CONFLICT (content): Merge conflict in test
Automatic merge failed; fix conflicts and then commit the result.

Now, we want to solve these conflicts with a graphical tool (in the example, it’s meld). git mergetool on the command line open the graphical tool of our choice.

sparsick@sparsick-ThinkPad-T430s > git mergetool


Normal merge conflict for 'test':
{local}: modified file
{remote}: modified file

After solving the merge conflicts, the change has to commit.

sparsick@sparsick-ThinkPad-T430s > git status

On branch master
All conflicts fixed but you are still merging.
(use "git commit" to conclude merge)

Changes to be committed:

modified:   test

Untracked files:
(use "git add <file>..." to include in what will be committed)

sparsick@sparsick-ThinkPad-T430s > git commit

You can see that we have a new untracked file test.orig . This is a backup of the merged file created by mergetool. You can configure that this backup should be removed after a successful merge.

sparsick@sparsick-ThinkPad-T430s > git config --global mergetool.keepBackup false

Further files are created when using git mergetool:

sparsick@sparsick-ThinkPad-T430s > git status

On branch master
Untracked files:
(use "git add ..." to include in what will be committed)


If only these files are currently untracked, then a git clean can help. Otherwise they have to be removed manually.

sparsick@sparsick-ThinkPad-T430s > git clean -f

Removing test.BACKUP.7344
Removing test.BASE.7344
Removing test.LOCAL.7344
Removing test.REMOTE.734


  1. Meld Homepage
  2. git mergetool Documentation

Leave a comment

Automatic Tomcat 8.5 Installation and Configuration as Windows Service

If you want to install Tomcat on Windows system as a service, you’ll get the recommendation to use the 32-/64-Bit Windows Service Installer. If you want to install the Tomcat manually, it’s fine. But you can’t use this installer for an automatic installation and configuration of Tomcat, because the installer is UI-based. The next sections explain how you can install and configure Tomcat on a CMD.

Tomcat Installation

  1. Download from the Apache Tomcat 8.5 download page the Core 64-bit Windows zip (or the 32-Bit zip).
  2. Unzip it (for example to C:\tomcat\)

That’s it. Now we have a ready-to-use Tomcat with default configuration values. But it isn’t install as a service.

Installation and Configuration As Windows Service

  1. Go to the bin folder in the installation folder of Tomcat (in the example  it’s C:\tomcat\apache-tomcat-8.5.11\bin)
  2. Install Tomcat as service named tomcat8 by calling service.bat install <servicename>
    C:\tomcat\apache-tomcat-8.5.11\bin>service.bat install tomcat8
  3.  tomcat8.exe //US//<servicename> followed by configuration parameter configures the Tomcat service. For example:
    C:\tomcat\apache-tomcat-8.5.11\bin>tomcat8.exe //US//tomcat8 --Startup=auto --JavaHome="C:\Program Files\Java\jre1.8.0_112" --JvmMs=2048 --JvmMx=4096 ++JvmOptions=-Dkey=value
  4. Start the Tomcat service with net start <servicename>
    net start tomcat8
  5. You can check on http://localhost:8080 whether Tomcat is installed correctly.

The configuration example (step 3) shows how to configure the JVM (heap space, Java option etc.), where Java is installed and which start type should use for the service. The full list of the possible configuration parameter for the Tomcat service can be found in the Apache Tomcat’s Windows Service documentation.

Now we have everything together for writing a Powershell script that does these steps automatically.


Leave a comment

Migration Sonatype Nexus 2 to Nexus 3

I’d like to share my experience with migration Sonatype Nexus 2 to Nexus 3.

Starting Point

I used two Nexus instances:

  • A Nexus 2 Instance for Maven Repositories (2.13)
  • A Nexus 3 Instance for Bower Repositories (3.0.1)

Both instances had several types of repositories (host, proxy, group). The reason for this set up was that Sonatype recommended not to use Nexus 3 (pre 3.1) for Maven repositories and Nexus 2 doesn’t support Bower repositories.

Migration Path

  1. Update Nexus 2 instance to version 2.14.1 (Update Guide)
  2. Update Nexus 3 Instance to version 3.1 (Update Guide ). It’s important that you migrate to the new working directory layout.
  3. Follow the migration step for upgrading from version 2 to version 3 (Update Guide).
    • My scenario was Nexus 2 and Nexus 3 running on the same system.
    • I selected as Upgrade Method “File system copy”
    • I chose that only “repository configuration content” has to migrate.
  4. After a successful migration I had to adjust some configuration in Nexus and in the system that use Nexus:
    • Setup in Nexus 3 a new user for deploying artifacts.
    • Adjust URL to the repositories in Jenkins, Maven settings and deployment scripts.

Leave a comment

Continuous Integration Infrastructure With Windows – Scripting With PowerShell

In one of my current project, I deal with how to run a Continuous Integration (CI) infrastructure on Windows machines. I have had experience in running a CI infrastructure for five years, but it was always on Linux machines. So in the next months I will write some blog posts about my challenge with Windows machines from the perspective of a Linux fan girl :-). This first blog post is about shell scripting on Windows. But bear in mind: This blog post isn’t a tutorial for PowerShell scripting. It only explains striking feature coming from Linux background.

When you run a CI infrastructure, it’s a frequent practice to write little shell scripts to automate repeatable tasks. On a Linux system you would write your scripts in Bash or in a script language like Perl or Phyton. I usually write my script in Bash or in Groovy. I choose Groovy, because I’m a Java Developer and it is possible writing Groovy script in Java-style in the beginning and the second argument for me was, that the administration of Jenkins is easier with Groovy scripts. Jenkins supports a Groovy console for administration tasks and build job’s step also can be automated with Groovy in Jenkins, directly. So I use Groovy for other automated tasks to not use so many script languages at the same time. Now you can say, ok, what’s the problem. It is able to use Groovy on Windows system. The problem is the requirements in my project. It is only allowed to use Java, C# or PowerShell as programming language. But I want to write little scripts, so from this point of view, only PowerShell remains.

Switch from Bash to PowerShell – What is striking

Good news at first, working with PowerShell isn’t as creepy as working with DOS shell. But it’s different compared to Bash shell. In next section, I will report what I notice when I start writing Powershell scripts with a Bash shell background.

First at all, it helps all lot for writing script in PowerShell from the Linux’s point of view when you understand the difference between how Linux and Windows handle their configuration. In Linux, you only have to adjust some text files to change system’s configuration. Here, the configuration is text driven and shells in Linux are optimized to handle text. On the other hand in Windows, you use an API to adjust properties in objects to change system’s configuration. Here, the configuration is API driven. The next important point is that Microsoft provides a large class library, the .NET framework, that has object model of Windows’ system configuration. PowerShell reuses this object model as the base for type system. So scripting in PowerShell feels more like object-oriented programming. Fortunately, we can reuse all functionality of the .NET framework in our PowerShell scripts. So if you’re familiar with C# programming, the start with PowerShell scripting is very easy for you. So writing scripts for PowerShell feels like working with an OOP language.

So let’s look at some code sample for typical situations to see the difference between scripting for Bash and for PowerShell.

Writing Something to the Standard Output Stream

On the Bash side, you have the built-in command echo for that:

echo "Hello World"

For PowerShell, you have a so-called Cmdlet Write-Output:

Write-Output "Hello World"

Now, we want to write the value of a variable to standard output.


message="Hello World"
echo $message


 $message="Hello World" Write-Output $message 

That was easy, wasn’t it

Parsing Files for a Pattern

In our example, we want to parse only XML files after a specific pattern (in our case “search-pattern”) and count how often this pattern is match in all.

On the Bash side, we use for Linux typically pipeline pattern. First, we use grep for searching the pattern and then we pipe the result of grep to wc to count the matches.

grep -w *.xml -e "search-pattern" | wc -l

On PowerShell, it looks little bit different. First, we have to list all XML files with dir. This result is piped to the Cmdlet ForEach-Object. This Cmdlet gets a script block. In our case, the script block reads the content of a file and pipe it to Select-String Cmdlet. This is responsible for filtering after the given pattern and this filter result is piped to Measure-Object that can calculate the numeric properties of object. In our case, it should only count the matches. At the end, every count has to be added together. The important thing is that the result of every Cmdlet is an object that has properties.

$sum = 0
dir *.xml | ForEach-Object {
$sum += (Get-Content $_ | Select-String -Pattern 'search-pattern' | Measure-Object).Count
Write-Output $sum


Conditions in Bash and in PowerShell look very similar . The only difference in my opinion is the using of bracket. In PowerShell, conditions look more like in C#.


if [ true -a true -o false -a 2 -gt 1 -a 1 -eq 1 -a 1 -lt 3 -a !false ];
  # do something if the condition is true
  # do something if the condition is false


if ( 1 -and ( 1 -or 0) -and (2 -gt 1) -and ( 1 -eq 1) -and (1 -lt 3) -and (-not 0)) {
  # do something if the condition is true
} else {
  # do something if the condition is false

Setting System Environment Variable

In both systems, Linux and Window, it is possible to set system environment variable on different context, system-wide, process or user based.


Setting system environment variable with Bash works like the following line:

export BASH_EXAMPLE="some value"

This variable is active during the current process. If you want that the variable is active system-wide, you have to edit the file /etc/profile or you create executable shell scripts in the directory /etc/profile.d. If the variable should be active only for special user session, you have to edit the file ~/.bash_profile . You can do this programmatically with sed or awk.

Using the variable works like that



In the PowerShell, you can call the .NET Framework API for setting the system environment variable.

[Environment]::SetEnvironmentVariable("WindowsExample", "Some value.", "Machine")

If the variable should be active only for special user or process, write User or Process instead of Machine. For process level there also exists another command:

$env:WindowsExample = "Some value."

The .Net Framework has a GetEnviromentVariable method for calling the variable.

[Environment]::GetEnvironmentVariable("WindowsExample", "Machine")
$Env:WindowsExample // shortcut

When setting system environment variable, you can see the big different between the both approach for configuration, text-driven (Linux) and API-driven (Windows).

Tool Support

I was pleasantly surprised when I figured out, that a PowerShell “IDE” exists, called PowerShell Integrated Scripting Environment (ISE). It supports you with code completion and has a complete documentation about the Cmdlets.

PowerShell Integration in Jenkins

One use case for writing scripts is running them in a build job in Jenkins. Jenkins can’t run PowerShell scripts out of the box. Fortunately, it exists a Jenkins Plugin for that called PowerShell Plugin. After installing this plugin a build step Windows PowerShell is avaible in the job configuration. This step has a command field where you can add your script code directly, or you add the path to your script (see below example). In the second variant it is important to add exit $LASTEXITCODE otherwise the build doesn’t recognize that the PowerShell script failed.


Further Information

  1. Manning’s book PowerShell in Action
  2. Jenkins’ PowerShell Plugin
  3. Microsoft TechNet about System Environment Variable.
  4. nixCraft’s blog post Linux: Set Environment Variable

1 Comment

Installation Cheat Sheet For LivingDoc

We wanted to evaluate the new Confluence plugin LivingDoc as a replacement to Fitnesse in order to execute automated web GUI tests.

  • Confluence 5.7.1 (Non-Cloud version)
  • Inside Confluence HSQL In-Memory DB for evaluation purpose
  • LivingDoc plugin 1.0.0.jar
  • Selenium Webdriver for automated Web Testing of
  • Spring Petclinic running inside a Tomcat 8
  • Java Version SDK
  • Maven 3.3.1

The following steps are an extension to the LivingDoc documentation. This documentation is very detailed, but if you struggle around some steps, check the following out. We recommend to use the search function of your browser to find the relevant parts. Additional this is not about Best Practices, but only about getting the setup running fast. Let´s start:

  1. Starting point is the LivingDoc Documentation under > Current Documentation > Getting Started
  2. After following this steps, go to Current Documentation > Confluence plugin
  3. Unfortunately, there is no direct link to the current livingdoc-confluence5-plugin.jar, so go to and choose livingdoc-confluence5-plugin-1.0.0.jar  (even there is already a version livingdoc-confluence5-plugin-1.1.0.jar) and download it.
  4. Next is the configuration of the Runner. Please look at the following pic:
    1_2016-03-06 14_20_37-LivingDoc Configuration - Confluence
    We replaced the classpath default value with the path, where you have downloaded the livingdoc-confluence5-plugin-1.0.0.jar on your machine.
  5. Next is Project Management:
    2_2016-03-06 16_37_48-LivingDoc Configuration - Confluence
    There, we choose our above prepared runner. Under classpaths, we copy the path to the jar of our Selenium project inclusive its dependencies. In step 7 below, it will described how this jar is built. This is needed so that the Selenium tests can be executed by LivingDoc.
  6. Next is remote agent: In order to get our scenario running, we got advice from the LivingDoc Developer Team to start a remote agent. You will get the complete remote agent jar under . At that time, we chose livingdoc-remote-agent-1.0.0-complete.jar and downloaded it. Please start it like it is written  in the Livingdoc documentation (Current Documentation > Confluence Plugin > Advanced > Remote Agent).
  7. In order that the Selenium tests are executed by LivingDoc, we need a jar file with all our tests inclusive their dependencies. Therefore, we use Maven Assembly Plugin to build a jar with all dependencies. Below the configuration of the Maven Assembly Plugin (Link to whole POM)

    The descriptor format jar-with-dependencies can be found in the Maven Assembly Plugin site.


Bringing all together and let the Remote Agent running, we can execute from Confluence our Selenium Test. Now we are ready to rumble.

In the meantime, there is a VirtualBox image with everything inside under LiviningDoc documentation > Showcases. Furthermore, there is a new release 1.1 that supports Confluence 5.9.3. But if you want to install all by yourself, we hope this can save you some time.

4_2016-03-06 15_05_28-PetClinic __ a Spring Framework demonstrationPetClinicOwners - AcceptanceTests


  1. Spring Pet Clinic Project
  2. LivingDoc on GitHub
  3. LivingDoc documentation