| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| |
| === 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.console</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 6/7 |
| |
| 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.6</target> |
| <source>1.6</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(buffer, cursor, 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. |