Custom distributions

As Karaf is an OSGi container, it's heavily used as an application and middleware kernel.

You may wish to construct your own Karaf distribution preconfigured to your requirements.
This custom distribution could contain:

  • branding to change the Karaf console look-and-feel
  • configuration files (in the etc folder) altered to your requirements
  • pre-packaged artifacts in the deploy folder
  • a pre-populated system repository (containing your own bundle and features descriptor)
  • renamed or specific scripts in the bin folder
  • system documentation files

Maven assembly

Basically a Karaf custom distribution involves:
1. Uncompressing a standard Karaf distribution in a given directory.
2. Populating the system repo with your features.
3. Populating the lib directory with your branding or other system bundle jar files.
4. Overriding the configuration files in the etc folder.

These tasks could be performed using scripting, or more easily and portable, using Apache Maven and a set of Maven plugins.

For instance, the Maven POM could look like:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM http://maven.apache.org/xsd/maven-4.0.0.xsd">

  <groupId>my.company</groupId>
  <artifactId>mycustom-karaf</artifactId>
  <version>1.0</version>
  <packaging>pom</packaging>
  <name>My Unix Custom Karaf Distribution</name>

  <properties>
    <karaf.version>2.2.2</karaf.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.apache.karaf</groupId>
      <artifactId>apache-karaf</artifactId>
      <version>2.3.7-SNAPSHOT</version>
      <type>tar.gz</type>
    </dependency>
    <dependency>
      <groupId>org.apache.karaf.assemblies.features</groupId>
      <artifactId>standard</artifactId>
      <version>2.3.7-SNAPSHOT</version>
      <type>xml</type>
      <classifier>features</classifier>
    </dependency>
  </dependencies>

  <build>
    <resources>
      <resource>
        <directory>/home/jbonofre/Workspace/karaf/manual/src/main/filtered-resources</directory>
        <filtering>true</filtering>
        <includes>
          <include>**/*</include>
        </includes>
      </resource>
    </resources>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <executions>
          <execution>
            <id>filter</id>
            <phase>generate-resources</phase>
            <goals>
              <goal>resources</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.apache.karaf.tooling</groupId>
        <artifactId>features-maven-plugin</artifactId>
        <version>2.3.7-SNAPSHOT</version>
        <executions>
          <execution>
           <id>add-features-to-repo</id>
           <phase>generate-resources</phase>
           <goals>
             <goal>add-features-to-repo</goal>
           </goals>
           <configuration>
              <descriptors>
                <descriptor>mvn:org.apache.karaf.assemblies.features/standard/2.3.7-SNAPSHOT/xml/features</descriptor>
                <descriptor>file:/home/jbonofre/Workspace/karaf/manual/target/classes/my-features.xml</descriptor>
              </descriptors>
              <features>
                <feature>my-feature</feature>
              </features>
           </configuration>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <executions>
          <execution>
            <id>copy</id>
            <phase>generate-resources</phase>
            <goals>
              <goal>copy</goal>
            </goals>
            <configuration>
               <!-- Define here the artifacts which should be considered in the assembly -->
               <!-- For instance, the branding jar -->
               <artifactItems>
                 <artifactItem>
                    <groupId>my.groupId</groupId>
                    <artifactId>my.branding.id</artifactId>
                    <version>1.0</version>
                    <outputDirectory>target/dependencies</outputDirectory>
                    <destFileName>mybranding.jar</destFileName>
                 </artifactItem>
               </artifactItems>
            </configuration>
          </execution>
          <execution>
            <!-- Uncompress the standard Karaf distribution -->
            <id>unpack</id>
            <phase>generate-resources</phase>
            <goals>
              <goal>unpack</goal>
            </goals>
            <configuration>
              <artifactItems>
                <artifactItem>
                  <groupId>org.apache.karaf</groupId>
                  <artifactId>apache-karaf</artifactId>
                  <type>tar.gz</type>
                  <outputDirectory>target/dependencies</outputDirectory>
                </artifactItem>
              </artifactItems>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-assembly-plugin</artifactId>
        <executions>
          <execution>
            <id>bin</id>
            <phase>package</phase>
            <goals>
              <goal>single</goal>
            </goals>
            <configuration>
              <descriptors>
                <descriptor>src/main/descriptors/bin.xml</descriptor>
              </descriptors>
              <appendAssemblyId>false</appendAssemblyId>
              <tarLongFileMode>gnu</tarLongFileMode>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

</project>

The Maven POM will download the Karaf standard distribution and prepare resources to be processed by the Maven assembly plugin.

Your Maven project structure should look like:

  • pom.xml: the previous POM file
  • src/main/descriptors/bin.xml: the assembly maven plugin descriptor (see below)
  • src/main/filtered-resources: contains all resource files that have Maven property values to be filtered/replaced. Typically, this will include features descriptor and configuration files.
  • src/main/distribution: contains all raw files which will be copied as-is into your custom distribution.

For instance, src/main/filtered-resources could contain:

  • my-features.xml where maven properties will be replaced
  • etc/org.apache.karaf.features.cfg file containing your my-features descriptor:
    #
    # Comma separated list of features repositories to register by default
    #
    featuresRepositories=mvn:org.apache.karaf/apache-karaf/2.3.7-SNAPSHOT/xml/features,mvn:my.groupId/my-features/2.3.7-SNAPSHOT/xml/features
    
    #
    # Comma separated list of features to install at startup
    #
    featuresBoot=config,ssh,management,my-feature
    

The src/main/distribution contains all your custom Karaf configuration files and scripts, as, for examples:

  • etc/org.ops4j.pax.logging.cfg
    # Root logger
    log4j.rootLogger=INFO, out, osgi:VmLogAppender
    log4j.throwableRenderer=org.apache.log4j.OsgiThrowableRenderer
    
    # CONSOLE appender not used by default
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32C %4L | %X{bundle.id} - %X{bundle.name} - %X{bundle.version} | %m%n
    
    # File appender
    log4j.appender.out=org.apache.log4j.RollingFileAppender
    log4j.appender.out.layout=org.apache.log4j.PatternLayout
    log4j.appender.out.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32C %4L | %X{bundle.id} - %X{bundle.name} - %X{bundle.version} | %m%n
    log4j.appender.out.file=${karaf.home}/log/my-customer-distribution.log
    log4j.appender.out.append=true
    log4j.appender.out.maxFileSize=1MB
    log4j.appender.out.maxBackupIndex=10
    
    # Sift appender
    log4j.appender.sift=org.apache.log4j.sift.MDCSiftingAppender
    log4j.appender.sift.key=bundle.name
    log4j.appender.sift.default=my-custom
    log4j.appender.sift.appender=org.apache.log4j.FileAppender
    log4j.appender.sift.appender.layout=org.apache.log4j.PatternLayout
    log4j.appender.sift.appender.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
    log4j.appender.sift.appender.file=${karaf.data}/log/$\\{bundle.name\\}.log
    log4j.appender.sift.appender.append=true
    
  • etc/system.properties
    #
    # The properties defined in this file will be made available through system
    # properties at the very beginning of the FAS boot process.
    #
    
    # Log level when the pax-logging service is not available
    # This level will only be used while the pax-logging service bundle
    # is not fully available.
    # To change log levels, please refer to the org.ops4j.pax.logging.cfg file
    # instead.
    org.ops4j.pax.logging.DefaultServiceLog.level=ERROR
    
    #
    # Name of this custom instance.
    #
    karaf.name=my-custom
    
    #
    # Default repository where bundles will be loaded from before using
    # other maven repositories. For the full Maven configuration, see the
    # org.ops4j.pax.url.mvn.cfg file.
    #
    karaf.default.repository=system
    
    #
    # Location of a shell script that will be run when starting a shell
    # session. This script can be used to create aliases and define
    # additional commands.
    #
    karaf.shell.init.script=${karaf.home}/etc/shell.init.script
    
    #
    # Set this empty property to avoid errors when validating xml documents.
    #
    xml.catalog.files=
    
    #
    # Suppress the bell in the console when hitting backspace to many times
    # for example
    #
    jline.nobell=true
    
    #
    # Default port for the OSGi HTTP Service
    #
    org.osgi.service.http.port=8181
    
    #
    # Allow usage of ${custom.home} as an alias for ${karaf.home}
    #
    custom.home=${karaf.home}
    
  • etc/users.properties
    admin=admin,admin
    
  • You can add a etc/custom.properties, it's a placeholder for any override you may need. For instance:
    karaf.systemBundlesStartLevel=50
    obr.repository.url=http://svn.apache.org/repos/asf/servicemix/smx4/obr-repo/repository.xml
    org.osgi.framework.system.packages.extra = \
      org.apache.karaf.branding; \
      com.sun.org.apache.xalan.internal.xsltc.trax; \
      com.sun.org.apache.xerces.internal.dom; \
      com.sun.org.apache.xerces.internal.jaxp; \
      com.sun.org.apache.xerces.internal.xni; \
      com.sun.jndi.ldap
    

Now, we can "assemble" our custom distribution using the Maven assembly plugin. The Maven assembly plugin uses an assembly descriptor, configured in POM above to be src/main/descriptors/bin.xml:

<assembly>

    <id>bin</id>

    <formats>
        <format>tar.gz</format>
    </formats>

    <fileSets>

        <!-- Expanded Karaf Standard Distribution -->
        <fileSet>
            <directory>target/dependencies/apache-karaf-2.3.7-SNAPSHOT</directory>
            <outputDirectory>/</outputDirectory>
            <excludes>
                <exclude>**/demos/**</exclude>
                <exclude>bin/**</exclude>
                <exclude>etc/system.properties</exclude>
                <exclude>etc/users.properties</exclude>
                <exclude>etc/org.apache.karaf.features.cfg</exclude>
                <exclude>etc/org.ops4j.pax.logging.cfg</exclude>
                <exclude>LICENSE</exclude>
                <exclude>NOTICE</exclude>
                <exclude>README</exclude>
                <exclude>RELEASE-NOTES</exclude>
                <exclude>karaf-manual*.html</exclude>
                <exclude>karaf-manual*.pdf</exclude>
            </excludes>
        </fileSet>

        <!-- Copy over bin/* separately to get the correct file mode -->
        <fileSet>
            <directory>target/dependencies/apache-karaf-2.3.7-SNAPSHOT</directory>
            <outputDirectory>/</outputDirectory>
            <includes>
                <include>bin/admin</include>
                <include>bin/karaf</include>
                <include>bin/start</include>
                <include>bin/stop</include>
            </includes>
            <fileMode>0755</fileMode>
        </fileSet>

        <!-- Copy over jar files -->
        <fileSet>
            <directory>target/dependencies</directory>
            <includes>
                <include>my-custom.jar</include>
            </includes>
            <outputDirectory>/lib/</outputDirectory>
        </fileSet>

        <fileSet>
            <directory>src/main/distribution</directory>
            <outputDirectory>/</outputDirectory>
            <fileMode>0644</fileMode>
        </fileSet>
        <fileSet>
            <directory>target/classes/etc</directory>
            <outputDirectory>/etc/</outputDirectory>
            <lineEnding>unix</lineEnding>
            <fileMode>0644</fileMode>
        </fileSet>

        <fileSet>
            <directory>target/features-repo</directory>
            <outputDirectory>/system</outputDirectory>
        </fileSet>

    </fileSets>

    <files>
        <file>
            <source>/home/jbonofre/Workspace/karaf/manual/target/dependencies/apache-karaf-2.3.7-SNAPSHOT/bin/karaf</source>
            <outputDirectory>/bin/</outputDirectory>
            <destName>my-custom</destName>
            <fileMode>0755</fileMode>
            <lineEnding>unix</lineEnding>
        </file>
        <file>
            <source>/home/jbonofre/Workspace/karaf/manual/target/classes/features.xml</source>
            <outputDirectory>/system/my.groupid/my-features/2.3.7-SNAPSHOT</outputDirectory>
            <destName>my-features-2.3.7-SNAPSHOT-features.xml</destName>
            <fileMode>0644</fileMode>
            <lineEnding>unix</lineEnding>
        </file>
    </files>

</assembly>

To build your custom Karaf distribution, just run:

mvn install

You will find your Karaf custom distribution tar.gz in the target directory.

Roadmap

A distribution goal is in preparation in the next Karaf

Custom distribution examples