RFC 147 Overview
The RFC-147 draft is not yet publicly available. It used to be called RFC-132, which can be found in an early specification draft
This is an overview of its main features:
Standard way to implement and run commands for any OSGi 4.2 framework
Commands are registered via service attributes, you don’t have to register a specific service. This allows commands to be registered by existing services, just by adding the new attributes:
Dictionary<String, Object> dict = new Hashtable<String, Object>();
dict.put(CommandProcessor.COMMAND_SCOPE, "shell");
dict.put(CommandProcessor.COMMAND_FUNCTION, new String[] {"sleep", "grep"});
context.registerService(name, service, dict);
Scope is used to provide a namespace for commands.
The commands above can be invoked as shell:sleep
and shell:grep
.
If the scope is omitted (e.g.
sleep
and grep
) then the first matching command is invoked.
Commands can have any signature
Arguments are coerced to call the best matching method using reflection.
A CommandSession
argument is inserted if required:
public void sleep(long millis) throws InterruptedException{
Thread.sleep(millis);
}
public void sleep(String[] args) throws Exception;
public boolean grep(CommandSession session, String[] args) throws Exception;
The CommandSession
interface provides methods for executing commands and getting and setting session variables:
public interface org.apache.felix.service.command.CommandSession {
Object execute(CharSequence commandline) throws Exception;
Object get(String name);
void put(String name, Object value);
...
}
Easy to use interactively - no unnecessary syntax
-
basic commands
g! echo hello world
hello world
-
session variables
g! msg = "hello world"
g! echo $msg
hello world
-
execution quotes
()
- similar to bash backquotes
g! (bundle 1) location
file:/Users/derek/Downloads/felix-framework-3.0.0/bundle/org.apache.felix.bundlerepository-1.6.2.jar
Lists, maps, pipes and closures
-
lists -
[]
g! list = [1 2 a b]
1
2
a
b
-
maps -
[]
g! map = [Jan=1 Feb=2 Mar=3]
Jan 1
Feb 2
Mar 3
-
pipes -
|
g! bundles | grep gogo
2|Active | 1|org.apache.felix.gogo.command (0.6.0)
3|Active | 1|org.apache.felix.gogo.runtime (0.6.0)
4|Active | 1|org.apache.felix.gogo.shell (0.6.0)
-
closures -
{}
g! echo2 = { echo xxx $args yyy }
g! echo2 hello world
xxx hello world yyy
Leverages existing Java capabilities, via reflection
-
exception handling - console shows summary, but full context available
g! start xxx
E: Cannot coerce start[xxx] to any of [(Bundle)]
g! $exception printstacktrace
java.lang.IllegalArgumentException: Cannot coerce start[xxx] to any of [(Bundle)]
at org.apache.felix.gogo.runtime.shell.Reflective.method(Reflective.java:162)
at org.apache.felix.gogo.runtime.shell.Command.execute(Command.java:40)
at org.apache.felix.gogo.runtime.shell.Closure.execute(Closure.java:211)
at org.apache.felix.gogo.runtime.shell.Closure.executeStatement(Closure.java:146)
at org.apache.felix.gogo.runtime.shell.Pipe.run(Pipe.java:91)
...
-
add all public methods on
java.lang.System
as commands:
g! addcommand system (loadClass java.lang.System)
g! system:getproperties
sun.io.unicode.encodingUnicodeLittle
java.version 1.5.0_19
java.class.path bin/felix.jar
java.awt.graphicsenvapple.awt.CGraphicsEnvironment
user.language en
sun.os.patch.level unknown
os.version 10.6.2
[snip]
Easy to implement and test commands without needing OSGi
Command implementations don’t need to reference any OSGi interfaces.
They can use System.in
, System.out
and System.err
, just as you would in a trivial Java application.
The ThreadIO
service transparently manages the singleton System.out
etc, so that each thread sees the appropriate stream:
public void cat(String[] args) throws Exception {
for (String arg : args) {
IOUtil.copy(arg, System.out);
}
}
Normal commands can provide control primitives
public void each(CommandSession session, Collection<Object> list, Function closure) throws Exception {
for (Object x : list) {
closure.execute(session, null);
}
}
then
g! each [Jan Feb Mar] { echo $it | grep . }
Jan
Feb
Mar
The default echo command returns a String and does not write to System.out. Also, by default, the console prints the results of each command, so echo appears to behave as you would expect. However, the console does not see the each closure above, so the result of echo would not be seen. This is why it is piped into grep, as the result of the command as well as its output is written to a pipeline. |