Scripting

In the console section of the users guide, we introduced the scripting support.

Assignation

You already know the first usage of scripting: execution of command.

karaf@root()> echo hello world
hello world

You can also assign a value to session variables:

karaf@root()> msg = "hello world"
hello world

Once you have assigned a value to a variable, you can display this value using the "resolved" variable name:

karaf@root()> echo $msg
hello world

The () are execution quotes (like the backquotes when you use bash on Unix).

karaf@root()> ($.context bundle 1) location
mvn:org.apache.karaf.jaas/org.apache.karaf.jaas.modules/4.0.0

The $.context access the context variables in the current session. We access to the bundle variable (an array containing all bundles), and we want to display the bundle location for the bundle at the index 1 in the bundle array.

Expressions

The shell has a built-in expression parser. Expressions must be enclosed with the %(…​) syntax.

Examples:

karaf@root()> %(1+2)
3
karaf@root()> a = 0
0
karaf@root()> %(a+=1)
1
karaf@root()> %(a+=1)
2
karaf@root()> b=1
1
karaf@root()> %(SQRT(a^2 + b^2))
1.7320508
Mathematical Operators
Operator Description

+

Additive operator

-

Subtraction operator

*

Multiplication operator

/

Division operator

%

Remainder operator (Modulo)

^

Power operator

Boolean Operators
Operator Description

=

Equals

==

Equals

!=

Not equals

<>

Not equals

<

Less than

Less than or equal to

>

Greater than

>=

Greater than or equal to

&&

Boolean and

||

Boolean or

Supported Functions
Function Description

NOT(expression)

Boolean negation, 1 (means true) if the expression is not zero

IF(condition,value_if_true,value_if_false)

Returns one value if the condition evaluates to true or the other if it evaluates to false

RANDOM()

Produces a random number between 0 and 1

MIN(e1,e2)

Returns the smaller of both expressions

MAX(e1,e2)

Returns the bigger of both expressions

ABS(expression)

Returns the absolute (non-negative) value of the expression

ROUND(expression,precision)

Rounds a value to a certain number of digits, uses the current rounding mode

FLOOR(expression)

Rounds the value down to the nearest integer

CEILING(expression)

Rounds the value up to the nearest integer

LOG(expression)

Returns the natural logarithm (base e) of an expression

SQRT(expression)

Returns the square root of an expression

SIN(expression)

Returns the trigonometric sine of an angle (in degrees)

COS(expression)

Returns the trigonometric cosine of an angle (in degrees)

TAN(expression)

Returns the trigonometric tangens of an angle (in degrees)

SINH(expression)

Returns the hyperbolic sine of a value

COSH(expression)

Returns the hyperbolic cosine of a value

TANH(expression)

Returns the hyperbolic tangens of a value

RAD(expression)

Converts an angle measured in degrees to an approximately equivalent angle measured in radians

DEG(expression)

Converts an angle measured in radians to an approximately equivalent angle measured in degrees

Functions names are case insensitive.

Supported Constants
Constant Description

PI

The value of PI, exact to 100 digits

TRUE

The value one

FALSE

The value zero

List, maps, pipes and closures

Using [], you can define an array variable:

karaf@root()> list = [1 2 a b]
1
2
a
b

You can also create a map if you put variables assignation in the array:

karaf@root()> map = [Jan=1 Feb=2 Mar=3]
Jan                 1
Feb                 2
Mar                 3

Using the | character, you can pipe output from a command as an input to another one.

For instance, you can access to the bundles context variables and send it as input to the grep command:

karaf@root()> ($.context bundles) | grep -i felix
    0|Active     |    0|org.apache.felix.framework (4.2.1)
   21|Active     |   11|org.apache.felix.fileinstall (3.2.6)
   43|Active     |   10|org.apache.felix.configadmin (1.6.0)
   51|Active     |   30|org.apache.felix.gogo.runtime (0.10.0)

You can assign a name to a script execution. It’s what we use for alias:

karaf@root()> echo2 = { echo xxx $args yyy }
echo xxx $args yyy
karaf@root()> echo2 hello world
xxx hello world yyy

Startup

The etc/shell.init.script file is executed at startup in each shell session, allowing the definition of additional variables or aliases or even complex functions. It’s like the bashrc or profile on Unix.

Constants and variables

Apache Karaf console provides a set of implicit constants and variables that you can use in your script.

  • $.context to access a bundle context

  • $.variables to access the list of defined variables

  • $.commands to access the list of defined commands

The variables starting with a # that are defined as Function (such as closures) will be executed automatically:

karaf@root> \#inc = { var = "${var}i" ; $var }
var = "${var}i" ; $var
karaf@root> echo $inc
i
karaf@root> echo $inc
ii
karaf@root>

Built-in variables and commands

Apache Karaf console provides built-in variables that are very useful for scripting:

  • $args retrieves the list of script parameters, given to the closure being executed

  • $1 .. $999 retrieves the nth argument of the closure

  • $it (same as $1) is used in a loop to access the current iterator value

Apache Karaf console provides commands for scripting:

  • shell:if

  • shell:new

  • shell:each

  • …​

See the full list of shell commands for details.

Leveraging existing Java capabilities (via reflection)

Apache Karaf console supports loading and execution of Java classes.

The $karaf.lastException implicit variable contains the latest Exception thrown.

karaf@root()> ($.context bundle) loadClass foo
Error executing command: foo not found by org.apache.karaf.shell.console [17]
karaf@root()> $karaf.lastException printStackTrace
java.lang.ClassNotFoundException: foo not found by org.apache.karaf.shell.console [17]
	at org.apache.felix.framework.BundleWiringImpl.findClassOrResourceByDelegation(BundleWiringImpl.java:1460)
	at org.apache.felix.framework.BundleWiringImpl.access$400(BundleWiringImpl.java:72)
	at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.loadClass(BundleWiringImpl.java:1843)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
	at org.apache.felix.framework.Felix.loadBundleClass(Felix.java:1723)
	at org.apache.felix.framework.BundleImpl.loadClass(BundleImpl.java:926)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at org.apache.felix.gogo.runtime.Reflective.invoke(Reflective.java:137)
	at org.apache.felix.gogo.runtime.Closure.executeMethod(Closure.java:527)
	at org.apache.felix.gogo.runtime.Closure.executeStatement(Closure.java:403)
	at org.apache.felix.gogo.runtime.Pipe.run(Pipe.java:108)
	at org.apache.felix.gogo.runtime.Closure.execute(Closure.java:183)
	at org.apache.felix.gogo.runtime.Closure.execute(Closure.java:120)
	at org.apache.felix.gogo.runtime.CommandSessionImpl.execute(CommandSessionImpl.java:89)
	at org.apache.karaf.shell.console.jline.Console.run(Console.java:166)
	at java.lang.Thread.run(Thread.java:680)

It’s possible to create objects to create commands "on the fly":

karaf@root()> addcommand system (($.context bundle) loadClass java.lang.System)
karaf@root()> system:getproperty karaf.name
root

It means that you can create an object using the new directive, and call methods on the objects:

karaf@root> map = (new java.util.HashMap)
karaf@root> $map put 0 0
karaf@root> $map
0                   0

Examples

The following examples show some scripts defined in etc/shell.init.script.

The first example shows a script to add a value into a configuration list:

#
# Add a value at the end of a property in the given OSGi configuration
#
# For example:
# > config-add-to-list org.ops4j.pax.url.mvn org.ops4j.pax.url.mvn.repositories http://scala-tools.org/repo-releases
#
config-add-to-list = {
  config:edit $1 ;
  a = (config:property-list | grep --color never $2 | tac) ;
  b = (echo $a | grep --color never "\b$3\b" | tac) ;
  if { ($b trim) isEmpty } {
    if { $a isEmpty } {
      config:property-set $2 $3
    } {
      config:property-append $2 ", $3"
    } ;
    config:update
  } {
    config:cancel
  }
}

This second example shows a script to wait for an OSGi service, up to a given timeout, and combine this script in other scripts:

#
# Wait for the given OSGi service to be available
#
wait-for-service-timeout = {
  _filter = $.context createFilter $1 ;
  _tracker = shell:new org.osgi.util.tracker.ServiceTracker $.context $_filter null ;
  $_tracker open ;
  _service = $_tracker waitForService $2 ;
  $_tracker close
}
#
# Wait for the given OSGi service to be available with a timeout of 10 seconds
#
wait-for-service = {
  wait-for-service-timeout $1 10000
}
#
# Wait for the given command to be available with a timeout of 10 seconds
# For example:
# > wait-for-command dev watch
#
wait-for-command = {
  wait-for-service "(&(objectClass=org.apache.felix.service.command.Function)(osgi.command.scope=$1)(osgi.command.function=$2))"
}