Extending
Apache Karaf is a very flexible container that you can extend very easily.
Console
In this section, you will see how to extend the console by adding your own command.
We will leverage Apache Maven to create and build the OSGi bundle. This OSGi bundle will use Blueprint. We don’t cover the details of OSGi bundle and Blueprint, see the specific sections for details.
Create the Maven project
To create the Maven project, we can:
-
use a Maven archetype
-
create by hand
Using archetype
The Maven Quickstart archetype can create an empty Maven project where you can put your project definition.
You can directly use:
mvn archetype:generate \ -DarchetypeArtifactId=maven-archetype-quickstart \ -DgroupId=org.apache.karaf.shell.samples \ -DartifactId=shell-sample-commands \ -Dversion=1.0-SNAPSHOT
It results to a ready to use project, including a pom.xml.
You can also use Maven archetype in interactive mode. You will have to answer to some questions used to generate
the project with the pom.xml:
mvn archetype:generate Choose a number: (1/2/3/4/5/6/7/.../32/33/34/35/36) 15: : 15 Define value for groupId: : org.apache.karaf.shell.samples Define value for artifactId: : shell-sample-commands Define value for version: 1.0-SNAPSHOT: : Define value for package: : org.apache.karaf.shell.samples
By hand
Alternatively, you can simply create the directory shell-sample-commands and create the pom.xml file inside it:
<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/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.karaf.shell.samples</groupId>
<artifactId>shell-sample-commands<artifactId>
<packaging>bundle</packaging>
<version>1.0-SNAPSHOT</version>
<name>shell-sample-commmands</name>
<dependencies>
<dependency>
<groupId>org.apache.karaf.shell</groupId>
<artifactId>org.apache.karaf.shell.core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>${felix.plugin.version}</version>
<configuration>
<instructions>
<Import-Package>
org.apache.felix.service.command,
org.apache.karaf.shell.commands,
org.apache.karaf.shell.console,
*
</Import-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
Configuring for Java 8
We are using annotations to define commands, so we need to ensure Maven will actually use JDK 1.6 or 1.7 to compile the jar.
Just add the following snippet after the dependencies section.
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<target>1.8</target>
<source>1.8</source>
</configuration>
</plugin>
</plugins>
</build>
Loading the project in your IDE
We can use Maven to generate the needed files for your IDE:
Inside the project, run the following command
mvn eclipse:eclipse
or
mvn idea:idea
The project files for your IDE should now be created. Just open the IDE and load the project.
Creating a basic command class
We can now create the command class HelloShellCommand.java
package org.apache.karaf.shell.samples;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Command;
import org.apache.karaf.shell.api.action.lifecycle.Service;
@Command(scope = "test", name = "hello", description="Says hello")
@Service
public class HelloShellCommand implements Action {
@Override
public Object execute() throws Exception {
System.out.println("Executing Hello command");
return null;
}
}
Manifest
In order for Karaf to find your command, you need to add the Karaf-Commands=* manifest header.
This is usually done by modifying the maven bundle plugin configuration
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
<Karaf-Commands>*</Karaf-Commands>
</instructions>
</configuration>
</plugin>
Compile
Let’s try to build the jar. Remove the test classes and sample classes if you used the artifact, then from the command line, run:
mvn install
The end of the maven output should look like:
[INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESSFUL [INFO] ------------------------------------------------------------------------
Test
Launch Apache Karaf and install your bundle:
karaf@root()> bundle:install -s mvn:org.apache.karaf.shell.samples/shell-sample-commands/1.0-SNAPSHOT
Let’s try running the command:
karaf@root()> test:hello Executing Hello command
Command completer
A completer allows you to automatically complete a command argument using <tab>. A completer is simply a bean which is injected to a command.
Of course to be able to complete it, the command should require an argument.
Command argument
We add an argument to the HelloCommand:
package org.apache.karaf.shell.samples;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
import org.apache.karaf.shell.api.action.Completion;
import org.apache.karaf.shell.api.action.lifecycle.Service;
@Command(scope = "test", name = "hello", description="Says hello")
@Service
public class HelloShellCommand implements Action {
@Argument(index = 0, name = "name", description = "The name that sends the greet.", required = true, multiValued = false)
@Completion(SimpleNameCompleter.class)
String name = null;
@Override
public Object execute() throws Exception {
System.out.println("Hello " + name);
return null;
}
}
Completer bean
A completer is a bean which implements the Completer interface:
package org.apache.karaf.shell.samples;
import org.apache.karaf.shell.api.action.lifecycle.Service;
import org.apache.karaf.shell.api.console.CommandLine;
import org.apache.karaf.shell.api.console.Completer;
import org.apache.karaf.shell.api.console.Session;
import org.apache.karaf.shell.support.completers.StringsCompleter;
/**
* <p>
* A very simple completer.
* </p>
*/
@Service
public class SimpleNameCompleter implements Completer {
public int complete(Session session, CommandLine commandLine, List<String> candidates) {
StringsCompleter delegate = new StringsCompleter();
delegate.getStrings().add("Mike");
delegate.getStrings().add("Eric");
delegate.getStrings().add("Jenny");
return delegate.complete(session, commandLine, candidates);
}
}
Completers for option values
Quite often your commands will not have just arguments, but also options. You can provide completers for option values. The snippet below shows the HelloShellCommand with an option to specify what the greet message will be.
package org.apache.karaf.shell.samples;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
import org.apache.karaf.shell.api.action.Completion;
import org.apache.karaf.shell.api.action.Option;
import org.apache.karaf.shell.api.action.lifecycle.Service;
@Command(scope = "test", name = "hello", description="Says hello")
@Service
public class HelloShellCommand implements Action {
@Argument(index = 0, name = "name", description = "The name that sends the greet.", required = true, multiValued = false)
@Completion(SimpleNameCompleter.class)
String name = null;
@Option(name = "-g", aliases = "--greet", description = "The configuration pid", required = false, multiValued = false)
@Completion(GreetCompleter.class)
String greet = "Hello;
@Override
public Object execute() throws Exception {
System.out.println(greet + " " + name);
return null;
}
}
Completers with state
Some times we want to tune the behavior of the completer depending on the commands already executed, in the current shell or even the rest of the arguments that have been already passed to the command. Such example is the config:set-property command which will provide auto completion for only for the properties of the pid specified by a previously issued config:edit command or by the option --pid.
The Session object provides map like methods for storing key/value pairs and can be used to put/get the state.
The pre-parsed CommandLine objects allows you to check the previous arguments and options on the command line and to fine tune
the behavior of the Completer.
Those two objects are given to the Completer when calling the complete method.
Test
Launch a Karaf instance and run the following command to install the newly created bundle:
karaf@root()> bundle:install -s mvn:org.apache.karaf.shell.samples/shell-sample-commands/1.0-SNAPSHOT
Let’s try running the command:
karaf@root> test:hello <tab> one two three
WebConsole
You can also extend the Apache Karaf WebConsole by providing and installing a webconsole plugin.
A plugin is an OSGi bundle that register a Servlet as an OSGi service with some webconsole properties.