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