You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by gn...@apache.org on 2014/03/05 16:08:25 UTC
[03/10] [KARAF-2805] Clean console and commands model
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/impl/console/ConsoleSessionImpl.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/ConsoleSessionImpl.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/ConsoleSessionImpl.java
new file mode 100644
index 0000000..7988035
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/ConsoleSessionImpl.java
@@ -0,0 +1,583 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.karaf.shell.impl.console;
+
+import java.io.CharArrayWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.InterruptedIOException;
+import java.io.PrintStream;
+import java.io.Reader;
+import java.lang.management.ManagementFactory;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import jline.UnsupportedTerminal;
+import jline.console.ConsoleReader;
+import jline.console.history.MemoryHistory;
+import jline.console.history.PersistentHistory;
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.felix.service.command.Converter;
+import org.apache.felix.service.command.Function;
+import org.apache.felix.service.threadio.ThreadIO;
+import org.apache.karaf.shell.api.console.Command;
+import org.apache.karaf.shell.api.console.Completer;
+import org.apache.karaf.shell.api.console.History;
+import org.apache.karaf.shell.api.console.Registry;
+import org.apache.karaf.shell.api.console.Session;
+import org.apache.karaf.shell.api.console.SessionFactory;
+import org.apache.karaf.shell.api.console.Terminal;
+import org.apache.karaf.shell.impl.console.parsing.Parser;
+import org.apache.karaf.shell.support.ShellUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ConsoleSessionImpl implements Session {
+
+ public static final String SHELL_INIT_SCRIPT = "karaf.shell.init.script";
+ public static final String SHELL_HISTORY_MAXSIZE = "karaf.shell.history.maxSize";
+ public static final String PROMPT = "PROMPT";
+ public static final String DEFAULT_PROMPT = "\u001B[1m${USER}\u001B[0m@${APPLICATION}(${SUBSHELL})> ";
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ConsoleSessionImpl.class);
+
+ // Input stream
+ final BlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(1024);
+ final ConsoleInputStream console = new ConsoleInputStream();
+ final Pipe pipe = new Pipe();
+ volatile boolean running;
+ volatile boolean eof;
+
+ final SessionFactory factory;
+ final ThreadIO threadIO;
+ final InputStream in;
+ final PrintStream out;
+ final PrintStream err;
+ private Runnable closeCallback;
+
+ final CommandSession session;
+ final Registry registry;
+ final Terminal terminal;
+ final History history;
+ final ConsoleReader reader;
+
+ private boolean interrupt;
+ private Thread thread;
+
+ public ConsoleSessionImpl(SessionFactory factory,
+ CommandProcessor processor,
+ ThreadIO threadIO,
+ InputStream in,
+ PrintStream out,
+ PrintStream err,
+ Terminal term,
+ String encoding,
+ Runnable closeCallback) {
+ // Arguments
+ this.factory = factory;
+ this.threadIO = threadIO;
+ this.in = in;
+ this.out = out;
+ this.err = err;
+ this.closeCallback = closeCallback;
+
+ // Terminal
+ terminal = term == null ? new JLineTerminal(new UnsupportedTerminal()) : term;
+
+
+ // Console reader
+ try {
+ reader = new ConsoleReader(null,
+ in != null ? console : null,
+ out,
+ terminal instanceof JLineTerminal ? ((JLineTerminal) terminal).getTerminal() : new KarafTerminal(terminal),
+ encoding);
+ } catch (IOException e) {
+ throw new RuntimeException("Error opening console reader", e);
+ }
+
+ // History
+ final File file = getHistoryFile();
+ try {
+ file.getParentFile().mkdirs();
+ reader.setHistory(new KarafFileHistory(file));
+ } catch (Exception e) {
+ LOGGER.error("Can not read history from file " + file + ". Using in memory history", e);
+ }
+ if (reader.getHistory() instanceof MemoryHistory) {
+ String maxSizeStr = System.getProperty(SHELL_HISTORY_MAXSIZE);
+ if (maxSizeStr != null) {
+ ((MemoryHistory) this.reader.getHistory()).setMaxSize(Integer.parseInt(maxSizeStr));
+ }
+ }
+ history = new HistoryWrapper(reader.getHistory());
+
+ // Registry
+ registry = new RegistryImpl(factory.getRegistry());
+ registry.register(factory);
+ registry.register(this);
+ registry.register(registry);
+ registry.register(terminal);
+ registry.register(history);
+
+ // Completers
+ Completer completer = new CommandsCompleter(factory);
+ reader.addCompleter(new CompleterAsCompletor(this, completer));
+ registry.register(completer);
+ registry.register(new CommandNamesCompleter());
+
+ // Session
+ session = processor.createSession(in != null ? console : null, out, err);
+ Properties sysProps = System.getProperties();
+ for (Object key : sysProps.keySet()) {
+ session.put(key.toString(), sysProps.get(key));
+ }
+ session.put(".session", this);
+ session.put(".commandSession", session);
+ session.put(".jline.reader", reader);
+ session.put(".jline.terminal", reader.getTerminal());
+ session.put(".jline.history", reader.getHistory());
+ session.put(Session.SCOPE, "shell:bundle:*");
+ session.put(Session.SUBSHELL, "");
+ session.put(Session.COMPLETION_MODE, loadCompletionMode());
+ session.put("USER", ShellUtil.getCurrentUserName());
+ session.put("APPLICATION", System.getProperty("karaf.name", "root"));
+ session.put("#LINES", new Function() {
+ public Object execute(CommandSession session, List<Object> arguments) throws Exception {
+ return Integer.toString(terminal.getHeight());
+ }
+ });
+ session.put("#COLUMNS", new Function() {
+ public Object execute(CommandSession session, List<Object> arguments) throws Exception {
+ return Integer.toString(terminal.getWidth());
+ }
+ });
+ session.put("pid", getPid());
+ }
+
+ /**
+ * Subclasses can override to use a different history file.
+ *
+ * @return
+ */
+ protected File getHistoryFile() {
+ String defaultHistoryPath = new File(System.getProperty("user.home"), ".karaf/karaf.history").toString();
+ return new File(System.getProperty("karaf.history", defaultHistoryPath));
+ }
+
+ @Override
+ public Terminal getTerminal() {
+ return terminal;
+ }
+
+ public History getHistory() {
+ return history;
+ }
+
+ @Override
+ public Registry getRegistry() {
+ return registry;
+ }
+
+ @Override
+ public SessionFactory getFactory() {
+ return factory;
+ }
+
+ public void close() {
+ if (!running) {
+ return;
+ }
+ if (reader.getHistory() instanceof PersistentHistory) {
+ try {
+ ((PersistentHistory) reader.getHistory()).flush();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ running = false;
+ pipe.interrupt();
+ if (closeCallback != null) {
+ closeCallback.run();
+ }
+ }
+
+ public void run() {
+ try {
+ threadIO.setStreams(session.getKeyboard(), out, err);
+ thread = Thread.currentThread();
+ running = true;
+ pipe.start();
+ Properties brandingProps = Branding.loadBrandingProperties(terminal);
+ welcome(brandingProps);
+ setSessionProperties(brandingProps);
+ String scriptFileName = System.getProperty(SHELL_INIT_SCRIPT);
+ executeScript(scriptFileName);
+ while (running) {
+ try {
+ String command = readAndParseCommand();
+ if (command == null) {
+ break;
+ }
+ //session.getConsole().println("Executing: " + line);
+ Object result = session.execute(command);
+ if (result != null) {
+ session.getConsole().println(session.format(result, Converter.INSPECT));
+ }
+ } catch (InterruptedIOException e) {
+ //System.err.println("^C");
+ // TODO: interrupt current thread
+ } catch (InterruptedException e) {
+ //interrupt current thread
+ } catch (Throwable t) {
+ ShellUtil.logException(this, t);
+ }
+ }
+ close();
+ } finally {
+ try {
+ threadIO.close();
+ } catch (Throwable t) {
+ // Ignore
+ }
+ }
+ }
+
+ @Override
+ public Object execute(CharSequence commandline) throws Exception {
+ return session.execute(commandline);
+ }
+
+ @Override
+ public Object get(String name) {
+ return session.get(name);
+ }
+
+ @Override
+ public void put(String name, Object value) {
+ session.put(name, value);
+ }
+
+ @Override
+ public InputStream getKeyboard() {
+ return session.getKeyboard();
+ }
+
+ @Override
+ public PrintStream getConsole() {
+ return session.getConsole();
+ }
+
+ @Override
+ public String resolveCommand(String name) {
+ // TODO: optimize
+ if (!name.contains(":")) {
+ String[] scopes = ((String) get(Session.SCOPE)).split(":");
+ List<Command> commands = registry.getCommands();
+ for (String scope : scopes) {
+ for (Command command : commands) {
+ if ((Session.SCOPE_GLOBAL.equals(scope) || command.getScope().equals(scope)) && command.getName().equals(name)) {
+ return command.getScope() + ":" + name;
+ }
+ }
+ }
+ }
+ return name;
+ }
+
+ @Override
+ public String readLine(String prompt, Character mask) throws IOException {
+ return reader.readLine(prompt, mask);
+ }
+
+ private String loadCompletionMode() {
+ String mode;
+ try {
+ File shellCfg = new File(System.getProperty("karaf.etc"), "/org.apache.karaf.shell.cfg");
+ Properties properties = new Properties();
+ properties.load(new FileInputStream(shellCfg));
+ mode = (String) properties.get("completionMode");
+ if (mode == null) {
+ LOGGER.debug("completionMode property is not defined in etc/org.apache.karaf.shell.cfg file. Using default completion mode.");
+ mode = Session.COMPLETION_MODE_GLOBAL;
+ }
+ } catch (Exception e) {
+ LOGGER.warn("Can't read {}/org.apache.karaf.shell.cfg file. The completion is set to default.", System.getProperty("karaf.etc"));
+ mode = Session.COMPLETION_MODE_GLOBAL;
+ }
+ return mode;
+ }
+
+ private String readAndParseCommand() throws IOException {
+ String command = null;
+ boolean loop = true;
+ boolean first = true;
+ while (loop) {
+ checkInterrupt();
+ String line = reader.readLine(first ? getPrompt() : "> ");
+ if (line == null) {
+ break;
+ }
+ if (command == null) {
+ command = line;
+ } else {
+ if (command.charAt(command.length() - 1) == '\\') {
+ command = command.substring(0, command.length() - 1) + line;
+ } else {
+ command += "\n" + line;
+ }
+ }
+ if (reader.getHistory().size() == 0) {
+ reader.getHistory().add(command);
+ } else {
+ // jline doesn't add blank lines to the history so we don't
+ // need to replace the command in jline's console history with
+ // an indented one
+ if (command.length() > 0 && !" ".equals(command)) {
+ reader.getHistory().replace(command);
+ }
+ }
+ if (command.length() > 0 && command.charAt(command.length() - 1) == '\\') {
+ loop = true;
+ first = false;
+ } else {
+ try {
+ Class<?> cl = CommandSession.class.getClassLoader().loadClass("org.apache.felix.gogo.runtime.Parser");
+ Object parser = cl.getConstructor(CharSequence.class).newInstance(command);
+ cl.getMethod("program").invoke(parser);
+ loop = false;
+ } catch (Exception e) {
+ loop = true;
+ first = false;
+ } catch (Throwable t) {
+ // Reflection problem ? just quit
+ loop = false;
+ }
+ }
+ }
+ return command;
+ }
+
+ private void executeScript(String scriptFileName) {
+ if (scriptFileName != null) {
+ Reader r = null;
+ try {
+ File scriptFile = new File(scriptFileName);
+ r = new InputStreamReader(new FileInputStream(scriptFile));
+ CharArrayWriter w = new CharArrayWriter();
+ int n;
+ char[] buf = new char[8192];
+ while ((n = r.read(buf)) > 0) {
+ w.write(buf, 0, n);
+ }
+ session.execute(new String(w.toCharArray()));
+ } catch (Exception e) {
+ LOGGER.debug("Error in initialization script", e);
+ System.err.println("Error in initialization script: " + e.getMessage());
+ } finally {
+ if (r != null) {
+ try {
+ r.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ }
+ }
+ }
+
+ protected void welcome(Properties brandingProps) {
+ String welcome = brandingProps.getProperty("welcome");
+ if (welcome != null && welcome.length() > 0) {
+ session.getConsole().println(welcome);
+ }
+ }
+
+ protected void setSessionProperties(Properties brandingProps) {
+ for (Map.Entry<Object, Object> entry : brandingProps.entrySet()) {
+ String key = (String) entry.getKey();
+ if (key.startsWith("session.")) {
+ session.put(key.substring("session.".length()), entry.getValue());
+ }
+ }
+ }
+
+ protected String getPrompt() {
+ try {
+ String prompt;
+ try {
+ Object p = session.get(PROMPT);
+ if (p != null) {
+ prompt = p.toString();
+ } else {
+ Properties properties = Branding.loadBrandingProperties(terminal);
+ if (properties.getProperty("prompt") != null) {
+ prompt = properties.getProperty("prompt");
+ // we put the PROMPT in ConsoleSession to avoid to read
+ // the properties file each time.
+ session.put(PROMPT, prompt);
+ } else {
+ prompt = DEFAULT_PROMPT;
+ }
+ }
+ } catch (Throwable t) {
+ prompt = DEFAULT_PROMPT;
+ }
+ Matcher matcher = Pattern.compile("\\$\\{([^}]+)\\}").matcher(prompt);
+ while (matcher.find()) {
+ Object rep = session.get(matcher.group(1));
+ if (rep != null) {
+ prompt = prompt.replace(matcher.group(0), rep.toString());
+ matcher.reset(prompt);
+ }
+ }
+ return prompt;
+ } catch (Throwable t) {
+ return "$ ";
+ }
+ }
+
+ private void checkInterrupt() throws IOException {
+ if (Thread.interrupted() || interrupt) {
+ interrupt = false;
+ throw new InterruptedIOException("Keyboard interruption");
+ }
+ }
+
+ private void interrupt() {
+ interrupt = true;
+ thread.interrupt();
+ }
+
+ private String getPid() {
+ String name = ManagementFactory.getRuntimeMXBean().getName();
+ String[] parts = name.split("@");
+ return parts[0];
+ }
+
+ private class ConsoleInputStream extends InputStream {
+ private int read(boolean wait) throws IOException {
+ if (!running) {
+ return -1;
+ }
+ checkInterrupt();
+ if (eof && queue.isEmpty()) {
+ return -1;
+ }
+ Integer i;
+ if (wait) {
+ try {
+ i = queue.take();
+ } catch (InterruptedException e) {
+ throw new InterruptedIOException();
+ }
+ checkInterrupt();
+ } else {
+ i = queue.poll();
+ }
+ if (i == null) {
+ return -1;
+ }
+ return i;
+ }
+
+ @Override
+ public int read() throws IOException {
+ return read(true);
+ }
+
+ @Override
+ public int read(byte b[], int off, int len) throws IOException {
+ if (b == null) {
+ throw new NullPointerException();
+ } else if (off < 0 || len < 0 || len > b.length - off) {
+ throw new IndexOutOfBoundsException();
+ } else if (len == 0) {
+ return 0;
+ }
+
+ int nb = 1;
+ int i = read(true);
+ if (i < 0) {
+ return -1;
+ }
+ b[off++] = (byte) i;
+ while (nb < len) {
+ i = read(false);
+ if (i < 0) {
+ return nb;
+ }
+ b[off++] = (byte) i;
+ nb++;
+ }
+ return nb;
+ }
+
+ @Override
+ public int available() throws IOException {
+ return queue.size();
+ }
+ }
+
+ private class Pipe extends Thread {
+ public Pipe() {
+ super("Karaf shell pipe thread");
+ setDaemon(true);
+ }
+
+ public void run() {
+ try {
+ while (running) {
+ try {
+ int c = in.read();
+ if (c == -1) {
+ return;
+ } else if (c == 4 && !ShellUtil.getBoolean(ConsoleSessionImpl.this, Session.IGNORE_INTERRUPTS)) {
+ err.println("^D");
+ return;
+ } else if (c == 3 && !ShellUtil.getBoolean(ConsoleSessionImpl.this, Session.IGNORE_INTERRUPTS)) {
+ err.println("^C");
+ reader.getCursorBuffer().clear();
+ ConsoleSessionImpl.this.interrupt();
+ }
+ queue.put(c);
+ } catch (Throwable t) {
+ return;
+ }
+ }
+ } finally {
+ eof = true;
+ try {
+ queue.put(-1);
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/impl/console/HeadlessSessionImpl.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/HeadlessSessionImpl.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/HeadlessSessionImpl.java
new file mode 100644
index 0000000..16e73ca
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/HeadlessSessionImpl.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.karaf.shell.impl.console;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.karaf.shell.api.console.Command;
+import org.apache.karaf.shell.api.console.History;
+import org.apache.karaf.shell.api.console.Registry;
+import org.apache.karaf.shell.api.console.Session;
+import org.apache.karaf.shell.api.console.SessionFactory;
+import org.apache.karaf.shell.api.console.Terminal;
+import org.apache.karaf.shell.support.ShellUtil;
+
+public class HeadlessSessionImpl implements Session {
+
+ final SessionFactory factory;
+ final CommandSession session;
+ final Registry registry;
+
+ public HeadlessSessionImpl(SessionFactory factory, CommandProcessor processor, InputStream in, PrintStream out, PrintStream err) {
+ // Factory
+ this.factory = factory;
+ // Registry
+ registry = new RegistryImpl(factory.getRegistry());
+ registry.register(factory);
+ registry.register(this);
+ registry.register(registry);
+ // Session
+ session = processor.createSession(in, out, err);
+ Properties sysProps = System.getProperties();
+ for (Object key : sysProps.keySet()) {
+ session.put(key.toString(), sysProps.get(key));
+ }
+ session.put(".session", this);
+ session.put(".commandSession", session);
+ session.put(Session.SCOPE, "shell:bundle:*");
+ session.put(Session.SUBSHELL, "");
+ session.put("USER", ShellUtil.getCurrentUserName());
+ session.put("APPLICATION", System.getProperty("karaf.name", "root"));
+ }
+
+ public CommandSession getSession() {
+ return session;
+ }
+
+ @Override
+ public Object execute(CharSequence commandline) throws Exception {
+ return session.execute(commandline);
+ }
+
+ @Override
+ public Object get(String name) {
+ return session.get(name);
+ }
+
+ @Override
+ public void put(String name, Object value) {
+ session.put(name, value);
+ }
+
+ @Override
+ public InputStream getKeyboard() {
+ return session.getKeyboard();
+ }
+
+ @Override
+ public PrintStream getConsole() {
+ return session.getConsole();
+ }
+
+ @Override
+ public String readLine(String prompt, Character mask) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Terminal getTerminal() {
+ return null;
+ }
+
+ @Override
+ public History getHistory() {
+ return null;
+ }
+
+ @Override
+ public Registry getRegistry() {
+ return registry;
+ }
+
+ @Override
+ public SessionFactory getFactory() {
+ return factory;
+ }
+
+ @Override
+ public void run() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String resolveCommand(String name) {
+ // TODO: optimize
+ if (!name.contains(":")) {
+ String[] scopes = ((String) get(Session.SCOPE)).split(":");
+ List<Command> commands = registry.getCommands();
+ for (String scope : scopes) {
+ for (Command command : commands) {
+ if ((Session.SCOPE_GLOBAL.equals(scope) || command.getScope().equals(scope)) && command.getName().equals(name)) {
+ return command.getScope() + ":" + name;
+ }
+ }
+ }
+ }
+ return name;
+ }
+
+ @Override
+ public void close() {
+ session.close();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/impl/console/HistoryWrapper.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/HistoryWrapper.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/HistoryWrapper.java
new file mode 100644
index 0000000..6f69991
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/HistoryWrapper.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.karaf.shell.impl.console;
+
+import org.apache.karaf.shell.api.console.History;
+
+public class HistoryWrapper implements History {
+
+ private final jline.console.history.History history;
+
+ public HistoryWrapper(jline.console.history.History history) {
+ this.history = history;
+ }
+
+ @Override
+ public void clear() {
+ history.clear();
+ }
+
+ public int first() {
+ return history.iterator().next().index() + 1;
+ }
+
+ public int last() {
+ return first() + history.size() - 1;
+ }
+
+ @Override
+ public CharSequence get(int index) {
+ return history.get(index - 1);
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/impl/console/JLineTerminal.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/JLineTerminal.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/JLineTerminal.java
new file mode 100644
index 0000000..d000885
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/JLineTerminal.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.karaf.shell.impl.console;
+
+import org.apache.karaf.shell.api.console.Terminal;
+
+/**
+* Created by gnodet on 27/02/14.
+*/
+public class JLineTerminal implements Terminal {
+
+ private final jline.Terminal terminal;
+
+ public JLineTerminal(jline.Terminal terminal) {
+ this.terminal = terminal;
+ }
+
+ public jline.Terminal getTerminal() {
+ return terminal;
+ }
+
+ @Override
+ public int getWidth() {
+ return terminal.getWidth();
+ }
+
+ @Override
+ public int getHeight() {
+ return terminal.getHeight();
+ }
+
+ @Override
+ public boolean isAnsiSupported() {
+ return terminal.isAnsiSupported();
+ }
+
+ @Override
+ public boolean isEchoEnabled() {
+ return terminal.isEchoEnabled();
+ }
+
+ @Override
+ public void setEchoEnabled(boolean enabled) {
+ terminal.setEchoEnabled(enabled);
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/impl/console/KarafFileHistory.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/KarafFileHistory.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/KarafFileHistory.java
new file mode 100644
index 0000000..aa4d2e4
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/KarafFileHistory.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.karaf.shell.impl.console;
+
+import java.io.File;
+import java.io.IOException;
+
+import jline.console.history.FileHistory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Override the FileHistory impl to trap failures due to the
+ * user does not having write access to the history file.
+ */
+public final class KarafFileHistory extends FileHistory {
+
+ static final Logger LOGGER = LoggerFactory.getLogger(KarafFileHistory.class);
+ boolean failed = false;
+
+ public KarafFileHistory(File file) throws IOException {
+ super(file);
+ }
+
+ @Override
+ public void flush() throws IOException {
+ if( !failed ) {
+ try {
+ super.flush();
+ } catch (IOException e) {
+ failed = true;
+ LOGGER.debug("Could not write history file: "+ getFile(), e);
+ }
+ }
+ }
+
+ @Override
+ public void purge() throws IOException {
+ if( !failed ) {
+ try {
+ super.purge();
+ } catch (IOException e) {
+ failed = true;
+ LOGGER.debug("Could not delete history file: "+ getFile(), e);
+ }
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/impl/console/KarafTerminal.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/KarafTerminal.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/KarafTerminal.java
new file mode 100644
index 0000000..0c8b0fe
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/KarafTerminal.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.karaf.shell.impl.console;
+
+import jline.TerminalSupport;
+import org.apache.karaf.shell.api.console.Terminal;
+
+public class KarafTerminal extends TerminalSupport {
+
+ private final Terminal terminal;
+
+ public KarafTerminal(Terminal terminal) {
+ super(true);
+ this.terminal = terminal;
+ }
+
+ @Override
+ public synchronized boolean isAnsiSupported() {
+ return terminal.isAnsiSupported();
+ }
+
+ @Override
+ public int getWidth() {
+ return terminal.getWidth();
+ }
+
+ @Override
+ public int getHeight() {
+ return terminal.getHeight();
+ }
+
+ @Override
+ public synchronized boolean isEchoEnabled() {
+ return terminal.isEchoEnabled();
+ }
+
+ @Override
+ public synchronized void setEchoEnabled(boolean enabled) {
+ terminal.setEchoEnabled(enabled);
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/impl/console/RegistryImpl.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/RegistryImpl.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/RegistryImpl.java
new file mode 100644
index 0000000..43ed27e
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/RegistryImpl.java
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.karaf.shell.impl.console;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+import org.apache.karaf.shell.api.console.Command;
+import org.apache.karaf.shell.api.console.Registry;
+
+public class RegistryImpl implements Registry {
+
+ private final Registry parent;
+ private final Map<Object, Object> services = new LinkedHashMap<Object, Object>();
+
+ public RegistryImpl(Registry parent) {
+ this.parent = parent;
+ }
+
+ @Override
+ public List<Command> getCommands() {
+ return getServices(Command.class);
+ }
+
+ @Override
+ public <T> void register(Callable<T> factory, Class<T> clazz) {
+ synchronized (services) {
+ services.put(factory, new Factory<T>(clazz, factory));
+ }
+ }
+
+ @Override
+ public void register(Object service) {
+ synchronized (services) {
+ services.put(service, service);
+ }
+ }
+
+ @Override
+ public void unregister(Object service) {
+ synchronized (services) {
+ services.remove(service);
+ }
+ }
+
+ @Override
+ public <T> T getService(Class<T> clazz) {
+ synchronized (services) {
+ for (Object service : services.values()) {
+ if (service instanceof Factory) {
+ if (clazz.isAssignableFrom(((Factory) service).clazz)) {
+ if (isVisible(service)) {
+ try {
+ return clazz.cast(((Factory) service).callable.call());
+ } catch (Exception e) {
+ // TODO: log exception
+ }
+ }
+ }
+ } else if (clazz.isInstance(service)) {
+ if (isVisible(service)) {
+ return clazz.cast(service);
+ }
+ }
+ }
+ }
+ if (parent != null) {
+ return parent.getService(clazz);
+ }
+ return null;
+ }
+
+ @Override
+ public <T> List<T> getServices(Class<T> clazz) {
+ List<T> list = new ArrayList<T>();
+ synchronized (services) {
+ for (Object service : services.values()) {
+ if (service instanceof Factory) {
+ if (clazz.isAssignableFrom(((Factory) service).clazz)) {
+ if (isVisible(service)) {
+ try {
+ list.add(clazz.cast(((Factory) service).callable.call()));
+ } catch (Exception e) {
+ // TODO: log exception
+ }
+ }
+ }
+ } else if (clazz.isInstance(service)) {
+ if (isVisible(service)) {
+ list.add(clazz.cast(service));
+ }
+ }
+ }
+ }
+ if (parent != null) {
+ list.addAll(parent.getServices(clazz));
+ }
+ return list;
+ }
+
+ @Override
+ public boolean hasService(Class<?> clazz) {
+ synchronized (services) {
+ for (Object service : services.values()) {
+ if (service instanceof Factory) {
+ if (clazz.isAssignableFrom(((Factory) service).clazz)) {
+ if (isVisible(service)) {
+ return true;
+ }
+ }
+ } else if (clazz.isInstance(service)) {
+ if (isVisible(service)) {
+ return true;
+ }
+ }
+ }
+ }
+ if (parent != null) {
+ return parent.hasService(clazz);
+ }
+ return false;
+ }
+
+ protected boolean isVisible(Object service) {
+ return true;
+ }
+
+ static class Factory<T> {
+
+ final Class<T> clazz;
+ final Callable<T> callable;
+
+ Factory(Class<T> clazz, Callable<T> callable) {
+ this.clazz = clazz;
+ this.callable = callable;
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/impl/console/SessionFactoryImpl.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/SessionFactoryImpl.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/SessionFactoryImpl.java
new file mode 100644
index 0000000..2f19499
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/SessionFactoryImpl.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.karaf.shell.impl.console;
+
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.lang.management.ManagementFactory;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.felix.gogo.runtime.CommandProcessorImpl;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.felix.service.command.Function;
+import org.apache.felix.service.threadio.ThreadIO;
+import org.apache.karaf.shell.api.console.Command;
+import org.apache.karaf.shell.api.console.Registry;
+import org.apache.karaf.shell.api.console.Session;
+import org.apache.karaf.shell.api.console.SessionFactory;
+import org.apache.karaf.shell.api.console.Terminal;
+import org.apache.karaf.shell.impl.console.commands.ExitCommand;
+import org.apache.karaf.shell.impl.console.commands.SubShellCommand;
+import org.apache.karaf.shell.impl.console.commands.help.HelpCommand;
+import org.apache.karaf.shell.support.ShellUtil;
+
+public class SessionFactoryImpl extends RegistryImpl implements SessionFactory, Registry {
+
+ final CommandProcessorImpl commandProcessor;
+ final ThreadIO threadIO;
+ final List<Session> sessions = new ArrayList<Session>();
+ final Map<String, SubShellCommand> subshells = new HashMap<String, SubShellCommand>();
+ boolean closed;
+
+ public SessionFactoryImpl(ThreadIO threadIO) {
+ super(null);
+ this.threadIO = threadIO;
+ commandProcessor = new CommandProcessorImpl(threadIO);
+ register(new ExitCommand());
+ new HelpCommand(this);
+ }
+
+ public CommandProcessorImpl getCommandProcessor() {
+ return commandProcessor;
+ }
+
+ @Override
+ public Registry getRegistry() {
+ return this;
+ }
+
+ @Override
+ public void register(Object service) {
+ if (service instanceof Command) {
+ Command command = (Command) service;
+ String scope = command.getScope();
+ String name = command.getName();
+ if (!Session.SCOPE_GLOBAL.equals(scope)) {
+ if (!subshells.containsKey(scope)) {
+ SubShellCommand subShell = new SubShellCommand(scope);
+ subshells.put(scope, subShell);
+ register(subShell);
+ }
+ subshells.get(scope).increment();
+ }
+ commandProcessor.addCommand(scope, wrap(command), name);
+ }
+ super.register(service);
+ }
+
+ protected Function wrap(Command command) {
+ return new CommandWrapper(command);
+ }
+
+ @Override
+ public void unregister(Object service) {
+ super.unregister(service);
+ if (service instanceof Command) {
+ Command command = (Command) service;
+ String scope = command.getScope();
+ String name = command.getName();
+ commandProcessor.removeCommand(scope, name);
+ if (!Session.SCOPE_GLOBAL.equals(scope)) {
+ if (subshells.get(scope).decrement() == 0) {
+ SubShellCommand subShell = subshells.remove(scope);
+ unregister(subShell);
+ }
+ }
+ }
+ }
+
+ @Override
+ public Session create(InputStream in, PrintStream out, PrintStream err, Terminal term, String encoding, Runnable closeCallback) {
+ synchronized (this) {
+ if (closed) {
+ throw new IllegalStateException("SessionFactory has been closed");
+ }
+ final Session session = new ConsoleSessionImpl(this, commandProcessor, threadIO, in, out, err, term, encoding, closeCallback);
+ sessions.add(session);
+ return session;
+ }
+ }
+
+ @Override
+ public Session create(InputStream in, PrintStream out, PrintStream err) {
+ synchronized (this) {
+ if (closed) {
+ throw new IllegalStateException("SessionFactory has been closed");
+ }
+ final Session session = new HeadlessSessionImpl(this, commandProcessor, in, out, err);
+ sessions.add(session);
+ return session;
+ }
+ }
+
+ public void stop() {
+ synchronized (this) {
+ closed = true;
+ for (Session session : sessions) {
+ session.close();
+ }
+ commandProcessor.stop();
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/impl/console/TerminalFactory.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/TerminalFactory.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/TerminalFactory.java
new file mode 100644
index 0000000..bae3a25
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/TerminalFactory.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.karaf.shell.impl.console;
+
+import jline.NoInterruptUnixTerminal;
+import jline.Terminal;
+
+public class TerminalFactory {
+
+ private Terminal term;
+
+ public synchronized Terminal getTerminal() throws Exception {
+ if (term == null) {
+ init();
+ }
+ return term;
+ }
+
+ public void init() throws Exception {
+ jline.TerminalFactory.registerFlavor(jline.TerminalFactory.Flavor.UNIX, NoInterruptUnixTerminal.class);
+ term = jline.TerminalFactory.create();
+ }
+
+ public synchronized void destroy() throws Exception {
+ if (term != null) {
+ term.restore();
+ term = null;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/ExitCommand.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/ExitCommand.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/ExitCommand.java
new file mode 100644
index 0000000..147d403
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/ExitCommand.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.karaf.shell.impl.console.commands;
+
+import org.apache.karaf.shell.api.console.Session;
+
+public class ExitCommand extends TopLevelCommand {
+
+ @Override
+ public String getName() {
+ return "exit";
+ }
+
+ @Override
+ public String getDescription() {
+ return "Exit from the current shell";
+ }
+
+ @Override
+ protected void doExecute(Session session) throws Exception {
+ // get the current sub-shell
+ String currentSubShell = (String) session.get(Session.SUBSHELL);
+ if (!currentSubShell.isEmpty()) {
+ if (currentSubShell.contains(":")) {
+ int index = currentSubShell.lastIndexOf(":");
+ session.put(Session.SUBSHELL, currentSubShell.substring(0, index));
+ } else {
+ session.put(Session.SUBSHELL, "");
+ }
+ String currentScope = (String) session.get(Session.SCOPE);
+ int index = currentScope.indexOf(":");
+ session.put(Session.SCOPE, currentScope.substring(index + 1));
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/SubShellCommand.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/SubShellCommand.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/SubShellCommand.java
new file mode 100644
index 0000000..d2826f3
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/SubShellCommand.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.karaf.shell.impl.console.commands;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.karaf.shell.api.console.Session;
+
+public class SubShellCommand extends TopLevelCommand {
+
+ private final String name;
+ private final AtomicInteger references = new AtomicInteger();
+
+ public SubShellCommand(String name) {
+ this.name = name;
+ }
+
+ public void increment() {
+ references.incrementAndGet();
+ }
+
+ public int decrement() {
+ return references.decrementAndGet();
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String getDescription() {
+ return "Enter the subshell";
+ }
+
+ @Override
+ protected void doExecute(Session session) throws Exception {
+ session.put(Session.SUBSHELL, name);
+ session.put(Session.SCOPE, name + ":" + session.get(Session.SCOPE));
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/TopLevelCommand.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/TopLevelCommand.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/TopLevelCommand.java
new file mode 100644
index 0000000..ad2c729
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/TopLevelCommand.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.karaf.shell.impl.console.commands;
+
+import java.io.PrintStream;
+import java.util.List;
+
+import org.apache.karaf.shell.api.console.Command;
+import org.apache.karaf.shell.api.console.Completer;
+import org.apache.karaf.shell.api.console.Session;
+import org.apache.karaf.shell.support.CommandException;
+
+import static org.apache.karaf.shell.support.ansi.SimpleAnsi.COLOR_DEFAULT;
+import static org.apache.karaf.shell.support.ansi.SimpleAnsi.COLOR_RED;
+import static org.apache.karaf.shell.support.ansi.SimpleAnsi.INTENSITY_BOLD;
+import static org.apache.karaf.shell.support.ansi.SimpleAnsi.INTENSITY_NORMAL;
+
+public abstract class TopLevelCommand implements Command {
+
+ @Override
+ public String getScope() {
+ return Session.SCOPE_GLOBAL;
+ }
+
+ @Override
+ public Completer getCompleter(boolean scoped) {
+ return null;
+// return new StringsCompleter(new String[] { getName() });
+ }
+
+ @Override
+ public Object execute(Session session, List<Object> arguments) throws Exception {
+ if (arguments.contains("--help")) {
+ printHelp(System.out);
+ return null;
+ }
+ if (!arguments.isEmpty()) {
+ String msg = COLOR_RED
+ + "Error executing command "
+ + INTENSITY_BOLD + getName() + INTENSITY_NORMAL
+ + COLOR_DEFAULT + ": " + "too many arguments specified";
+ throw new CommandException(msg);
+ }
+ doExecute(session);
+ return null;
+ }
+
+ protected void printHelp(PrintStream out) {
+ out.println(INTENSITY_BOLD + "DESCRIPTION" + INTENSITY_NORMAL);
+ out.print(" ");
+ out.println(INTENSITY_BOLD + getName() + INTENSITY_NORMAL);
+ out.println();
+ out.print("\t");
+ out.println(getDescription());
+ out.println();
+ out.println(INTENSITY_BOLD + "SYNTAX" + INTENSITY_NORMAL);
+ out.print(" ");
+ out.println(getName() + " [options]");
+ out.println();
+ out.println(INTENSITY_BOLD + "OPTIONS" + INTENSITY_NORMAL);
+ out.print(" ");
+ out.println(INTENSITY_BOLD + "--help" + INTENSITY_NORMAL);
+ out.print(" ");
+ out.println("Display this help message");
+ out.println();
+ }
+
+ protected abstract void doExecute(Session session) throws Exception;
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/help/CommandListHelpProvider.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/help/CommandListHelpProvider.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/help/CommandListHelpProvider.java
new file mode 100644
index 0000000..9101520
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/help/CommandListHelpProvider.java
@@ -0,0 +1,117 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.apache.karaf.shell.impl.console.commands.help;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.apache.karaf.shell.api.console.Command;
+import org.apache.karaf.shell.api.console.Session;
+import org.apache.karaf.shell.api.console.Terminal;
+import org.apache.karaf.shell.support.NameScoping;
+import org.apache.karaf.shell.support.ansi.SimpleAnsi;
+import org.apache.karaf.shell.support.table.Col;
+import org.apache.karaf.shell.support.table.ShellTable;
+
+public class CommandListHelpProvider implements HelpProvider {
+
+ public String getHelp(Session session, String path) {
+ if (path.indexOf('|') > 0) {
+ if (path.startsWith("command-list|")) {
+ path = path.substring("command-list|".length());
+ } else {
+ return null;
+ }
+ }
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ SortedMap<String, String> commands = getCommandDescriptions(session, path);
+ if (commands.isEmpty()) {
+ return null;
+ } else if (commands.size() == 1 && commands.containsKey(path)) {
+ return null;
+ } else {
+ printMethodList(session, new PrintStream(baos), commands);
+ return baos.toString();
+ }
+ }
+
+ private SortedMap<String, String> getCommandDescriptions(Session session, String path) {
+ // TODO: this is not really clean
+
+ List<Command> commands = session.getRegistry().getCommands();
+
+ String subshell = (String) session.get(Session.SUBSHELL);
+ String completionMode = (String) session.get(Session.COMPLETION_MODE);
+
+ SortedMap<String,String> descriptions = new TreeMap<String,String>();
+ for (Command command : commands) {
+
+ String name = command.getScope() + ":" + command.getName();
+
+ if (command != null && !name.startsWith(path)) {
+ continue;
+ }
+
+ if (completionMode != null && completionMode.equalsIgnoreCase(Session.COMPLETION_MODE_SUBSHELL)) {
+ // filter the help only for "global" commands
+ if (subshell == null || subshell.trim().isEmpty()) {
+ if (!name.startsWith(Session.SCOPE_GLOBAL)) {
+ continue;
+ }
+ }
+ }
+
+ if (completionMode != null && (completionMode.equalsIgnoreCase(Session.COMPLETION_MODE_SUBSHELL)
+ || completionMode.equalsIgnoreCase(Session.COMPLETION_MODE_FIRST))) {
+ // filter the help only for commands local to the subshell
+ if (!name.startsWith(subshell)) {
+ continue;
+ }
+ }
+
+ String description = command.getDescription();
+ if (name.startsWith("*:")) {
+ name = name.substring(2);
+ }
+ if (subshell != null && !subshell.trim().isEmpty() && name.startsWith(subshell + ":")) {
+ name = name.substring(subshell.length() + 1);
+ }
+ descriptions.put(name, description);
+ }
+ return descriptions;
+ }
+
+ protected void printMethodList(Session session, PrintStream out, SortedMap<String, String> commands) {
+ Terminal term = session.getTerminal();
+ int termWidth = term != null ? term.getWidth() : 80;
+ out.println(SimpleAnsi.INTENSITY_BOLD + "COMMANDS" + SimpleAnsi.INTENSITY_NORMAL);
+ ShellTable table = new ShellTable().noHeaders().separator(" ").size(termWidth);
+ table.column(new Col("Command").maxSize(35));
+ table.column(new Col("Description"));
+ for (Map.Entry<String,String> entry : commands.entrySet()) {
+ String key = NameScoping.getCommandNameWithoutGlobalPrefix(session, entry.getKey());
+ table.addRow().addContent(key, entry.getValue());
+ }
+ table.print(out, true);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/help/HelpCommand.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/help/HelpCommand.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/help/HelpCommand.java
new file mode 100644
index 0000000..086b83b
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/help/HelpCommand.java
@@ -0,0 +1,225 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.karaf.shell.impl.console.commands.help;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.karaf.util.properties.InterpolationHelper;
+import org.apache.karaf.shell.api.console.Command;
+import org.apache.karaf.shell.api.console.CommandLine;
+import org.apache.karaf.shell.api.console.Completer;
+import org.apache.karaf.shell.api.console.Registry;
+import org.apache.karaf.shell.api.console.Session;
+import org.apache.karaf.shell.api.console.SessionFactory;
+import org.apache.karaf.shell.support.CommandException;
+import org.apache.karaf.shell.support.completers.StringsCompleter;
+
+import static org.apache.karaf.shell.support.ansi.SimpleAnsi.COLOR_DEFAULT;
+import static org.apache.karaf.shell.support.ansi.SimpleAnsi.COLOR_RED;
+import static org.apache.karaf.shell.support.ansi.SimpleAnsi.INTENSITY_BOLD;
+import static org.apache.karaf.shell.support.ansi.SimpleAnsi.INTENSITY_NORMAL;
+
+public class HelpCommand implements Command {
+
+ public HelpCommand(SessionFactory factory) {
+ Registry registry = factory.getRegistry();
+ registry.register(this);
+ registry.register(new SimpleHelpProvider());
+ registry.register(new CommandListHelpProvider());
+ registry.register(new SingleCommandHelpProvider());
+ }
+
+ @Override
+ public String getScope() {
+ return Session.SCOPE_GLOBAL;
+ }
+
+ @Override
+ public String getName() {
+ return "help";
+ }
+
+ @Override
+ public String getDescription() {
+ return "Displays this help or help about a command";
+ }
+
+ @Override
+ public Object execute(Session session, List<Object> arguments) throws Exception {
+ if (arguments.contains("--help")) {
+ printHelp(System.out);
+ return null;
+ }
+ if (arguments.size() > 1) {
+ String msg = COLOR_RED
+ + "Error executing command "
+ + INTENSITY_BOLD + getName() + INTENSITY_NORMAL
+ + COLOR_DEFAULT + ": " + "too many arguments specified";
+ throw new CommandException(msg);
+ }
+ String path = arguments.isEmpty() ? null : arguments.get(0) == null ? null : arguments.get(0).toString();
+ String help = getHelp(session, path);
+ if (help != null) {
+ System.out.println(help);
+ }
+ return null;
+ }
+
+ @Override
+ public Completer getCompleter(final boolean scoped) {
+ return new Completer() {
+ @Override
+ public int complete(Session session, CommandLine commandLine, List<String> candidates) {
+ String[] args = commandLine.getArguments();
+ int argIndex = commandLine.getCursorArgumentIndex();
+ StringsCompleter completer = new StringsCompleter(Collections.singletonList(getName()));
+ if (argIndex == 0) {
+ return completer.complete(session, new ArgumentCommandLine(args[argIndex], commandLine.getArgumentPosition()), candidates);
+ } else if (!verifyCompleter(session, completer, args[0])) {
+ return -1;
+ }
+ // TODO: use CommandNamesCompleter and better completion wrt parsing etc...
+ completer = new StringsCompleter();
+ for (Command command : session.getRegistry().getCommands()) {
+ if (!Session.SCOPE_GLOBAL.equals(command.getScope())) {
+ completer.getStrings().add(command.getScope() + ":" + command.getName());
+ }
+ completer.getStrings().add(command.getName());
+ }
+ completer.getStrings().add("--help");
+ if (argIndex == 1) {
+ int res;
+ if (argIndex < args.length) {
+ res = completer.complete(session, new ArgumentCommandLine(args[argIndex], commandLine.getArgumentPosition()), candidates);
+ } else {
+ res = completer.complete(session, new ArgumentCommandLine("", 0), candidates);
+ }
+ return res + (commandLine.getBufferPosition() - commandLine.getArgumentPosition());
+ } else if (!verifyCompleter(session, completer, args[1])) {
+ return -1;
+ }
+ return -1;
+ }
+ protected boolean verifyCompleter(Session session, Completer completer, String argument) {
+ List<String> candidates = new ArrayList<String>();
+ return completer.complete(session, new ArgumentCommandLine(argument, argument.length()), candidates) != -1 && !candidates.isEmpty();
+ }
+ };
+ }
+
+ protected void printHelp(PrintStream out) {
+ out.println(INTENSITY_BOLD + "DESCRIPTION" + INTENSITY_NORMAL);
+ out.print(" ");
+ out.println(INTENSITY_BOLD + getName() + INTENSITY_NORMAL);
+ out.println();
+ out.print("\t");
+ out.println(getDescription());
+ out.println();
+ out.println(INTENSITY_BOLD + "SYNTAX" + INTENSITY_NORMAL);
+ out.print(" ");
+ out.println(getName() + " [options] [command]");
+ out.println();
+ out.println(INTENSITY_BOLD + "ARGUMENTS" + INTENSITY_NORMAL);
+ out.print(" ");
+ out.println(INTENSITY_BOLD + "command" + INTENSITY_NORMAL);
+ out.print(" ");
+ out.println("Command to display help for");
+ out.println();
+ out.println(INTENSITY_BOLD + "OPTIONS" + INTENSITY_NORMAL);
+ out.print(" ");
+ out.println(INTENSITY_BOLD + "--help" + INTENSITY_NORMAL);
+ out.print(" ");
+ out.println("Display this help message");
+ out.println();
+ }
+
+ public String getHelp(final Session session, String path) {
+ if (path == null) {
+ path = "%root%";
+ }
+ Map<String,String> props = new HashMap<String,String>();
+ props.put("data", "${" + path + "}");
+ final List<HelpProvider> providers = session.getRegistry().getServices(HelpProvider.class);
+ InterpolationHelper.performSubstitution(props, new InterpolationHelper.SubstitutionCallback() {
+ public String getValue(final String key) {
+ for (HelpProvider hp : providers) {
+ String result = hp.getHelp(session, key);
+ if (result != null) {
+ return removeNewLine(result);
+ }
+ }
+ return null;
+ }
+ });
+ return props.get("data");
+ }
+
+ private String removeNewLine(String help) {
+ if (help != null && help.endsWith("\n")) {
+ help = help.substring(0, help.length() -1);
+ }
+ return help;
+ }
+
+ static class ArgumentCommandLine implements CommandLine {
+ private final String argument;
+ private final int position;
+
+ ArgumentCommandLine(String argument, int position) {
+ this.argument = argument;
+ this.position = position;
+ }
+
+ @Override
+ public int getCursorArgumentIndex() {
+ return 0;
+ }
+
+ @Override
+ public String getCursorArgument() {
+ return argument;
+ }
+
+ @Override
+ public int getArgumentPosition() {
+ return position;
+ }
+
+ @Override
+ public String[] getArguments() {
+ return new String[] { argument };
+ }
+
+ @Override
+ public int getBufferPosition() {
+ return position;
+ }
+
+ @Override
+ public String getBuffer() {
+ return argument;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/help/HelpProvider.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/help/HelpProvider.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/help/HelpProvider.java
new file mode 100644
index 0000000..ad5e215
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/help/HelpProvider.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.karaf.shell.impl.console.commands.help;
+
+import org.apache.karaf.shell.api.console.Session;
+
+public interface HelpProvider {
+
+ String getHelp(Session session, String path);
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/help/SimpleHelpProvider.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/help/SimpleHelpProvider.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/help/SimpleHelpProvider.java
new file mode 100644
index 0000000..90ad506
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/help/SimpleHelpProvider.java
@@ -0,0 +1,47 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.apache.karaf.shell.impl.console.commands.help;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.karaf.shell.api.console.Session;
+
+
+public class SimpleHelpProvider implements HelpProvider {
+
+ private final Map<String, String> help;
+
+ public SimpleHelpProvider() {
+ help = new HashMap<String, String>();
+ help.put("%root%", "${command-list|}");
+ help.put("all", "${command-list|}");
+ }
+
+ public String getHelp(Session session, String path) {
+ if (path.indexOf('|') > 0) {
+ if (path.startsWith("simple|")) {
+ path = path.substring("simple|".length());
+ } else {
+ return null;
+ }
+ }
+ return help.get(path);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/help/SingleCommandHelpProvider.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/help/SingleCommandHelpProvider.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/help/SingleCommandHelpProvider.java
new file mode 100644
index 0000000..dafab98
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/commands/help/SingleCommandHelpProvider.java
@@ -0,0 +1,54 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.apache.karaf.shell.impl.console.commands.help;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import org.apache.karaf.shell.api.console.Session;
+
+public class SingleCommandHelpProvider implements HelpProvider {
+
+ public String getHelp(Session session, String path) {
+ if (path.indexOf('|') > 0) {
+ if (path.startsWith("command|")) {
+ path = path.substring("command|".length());
+ } else {
+ return null;
+ }
+ }
+
+ String resolved = session.resolveCommand(path);
+
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(baos, true);
+ Session s = session.getFactory().create(null, ps, ps);
+ s.put(Session.SCOPE, session.get(Session.SCOPE));
+ s.put(Session.SUBSHELL, session.get(Session.SUBSHELL));
+ try {
+ s.execute(path + " --help");
+ } catch (Throwable t) {
+ return null;
+ } finally {
+ s.close();
+ }
+ return baos.toString();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/impl/console/osgi/Activator.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/osgi/Activator.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/osgi/Activator.java
new file mode 100644
index 0000000..921dca3
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/osgi/Activator.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.karaf.shell.impl.console.osgi;
+
+import org.apache.felix.gogo.runtime.threadio.ThreadIOImpl;
+import org.apache.karaf.shell.api.console.SessionFactory;
+import org.apache.karaf.shell.impl.action.osgi.CommandExtender;
+import org.apache.karaf.shell.impl.action.command.ManagerImpl;
+import org.apache.karaf.shell.impl.console.SessionFactoryImpl;
+import org.apache.karaf.shell.impl.console.TerminalFactory;
+import org.apache.karaf.shell.impl.console.osgi.secured.SecuredSessionFactoryImpl;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+public class Activator implements BundleActivator {
+
+ private static final String START_CONSOLE = "karaf.startLocalConsole";
+
+ private ThreadIOImpl threadIO;
+
+ private SessionFactoryImpl sessionFactory;
+ private ServiceRegistration sessionFactoryRegistration;
+
+ private CommandExtender actionExtender;
+
+ private TerminalFactory terminalFactory;
+ private LocalConsoleManager localConsoleManager;
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ threadIO = new ThreadIOImpl();
+ threadIO.start();
+
+ sessionFactory = new SecuredSessionFactoryImpl(context, threadIO);
+ sessionFactory.getCommandProcessor().addConverter(new Converters(context));
+ sessionFactory.getCommandProcessor().addConstant(".context", context.getBundle(0).getBundleContext());
+
+ sessionFactory.register(new ManagerImpl(sessionFactory, sessionFactory));
+
+ sessionFactoryRegistration = context.registerService(SessionFactory.class.getName(), sessionFactory, null);
+
+ actionExtender = new CommandExtender(sessionFactory);
+ actionExtender.start(context);
+
+ if (Boolean.parseBoolean(context.getProperty(START_CONSOLE))) {
+ terminalFactory = new TerminalFactory();
+ localConsoleManager = new LocalConsoleManager(context, terminalFactory, sessionFactory);
+ localConsoleManager.start();
+ }
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ sessionFactoryRegistration.unregister();
+ sessionFactory.stop();
+ localConsoleManager.stop();
+ actionExtender.stop(context);
+ threadIO.stop();
+ terminalFactory.destroy();
+ }
+}