FELIX-3530 Replace JLine with adapted AjaxTerm
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1345205 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole-plugins/gogo/pom.xml b/webconsole-plugins/gogo/pom.xml
index e155deb..c1f2614 100644
--- a/webconsole-plugins/gogo/pom.xml
+++ b/webconsole-plugins/gogo/pom.xml
@@ -78,19 +78,6 @@
<Bundle-Activator>
org.apache.felix.webconsole.plugins.gogo.impl.Activator
</Bundle-Activator>
- <Embed-Dependency>
- jline;inline=true
- </Embed-Dependency>
- <Bundle-NativeCode>
- META-INF/native/windows32/jansi.dll;osname=Win32;processor=x86,
- META-INF/native/windows64/jansi.dll;osname=Win32;processor=x86-64,
- <!--
- META-INF/native/linux32/libjansi.so;osname=Linux;processor=x86,
- META-INF/native/linux64/libjansi.so;osname=Linux;processor=x86-64,
- META-INF/native/osx/libjansi.jnilib;osname=MacOSX,
- -->
- *
- </Bundle-NativeCode>
</instructions>
</configuration>
</plugin>
@@ -128,11 +115,5 @@
<version>2.3</version>
<scope>provided</scope>
</dependency>
- <dependency>
- <groupId>org.sonatype.jline</groupId>
- <artifactId>jline</artifactId>
- <version>2.5</version>
- <scope>provided</scope>
- </dependency>
</dependencies>
</project>
diff --git a/webconsole-plugins/gogo/src/main/appended-resources/META-INF/DEPENDENCIES b/webconsole-plugins/gogo/src/main/appended-resources/META-INF/DEPENDENCIES
index bbd3414..81ae103 100644
--- a/webconsole-plugins/gogo/src/main/appended-resources/META-INF/DEPENDENCIES
+++ b/webconsole-plugins/gogo/src/main/appended-resources/META-INF/DEPENDENCIES
@@ -5,10 +5,9 @@
The Apache Software Foundation (http://www.apache.org/).
Licensed under the Apache License 2.0.
-This product includes software from https://github.com/jline/jline2
-Copyright (c) 2002-2006, Marc Prud'hommeaux <mwp1@cornell.edu>
-All rights reserved.
-Licensed under the BSD License
+This product includes files derived from Ajaxterm
+http://antony.lesuisse.org/software/ajaxterm/
+Ajaxterm files are released in the Public Domain
This plugin draws from Apache Karaf Console and Web Console bundles.
Kudos!
diff --git a/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/Activator.java b/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/Activator.java
index 59703ee..5f60721 100644
--- a/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/Activator.java
+++ b/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/Activator.java
@@ -18,18 +18,18 @@
*/
package org.apache.felix.webconsole.plugins.gogo.impl;
-import org.fusesource.jansi.AnsiConsole;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
public class Activator implements BundleActivator {
+ private SessionTerminalManager terminalManager;
+
private GogoPlugin plugin;
public void start(BundleContext context) throws Exception {
- AnsiConsole.systemInstall();
-
- this.plugin = new GogoPlugin();
+ this.terminalManager = new SessionTerminalManager(context);
+ this.plugin = new GogoPlugin(this.terminalManager);
this.plugin.register(context);
}
@@ -38,8 +38,10 @@
this.plugin.unregister();
this.plugin = null;
}
-
- AnsiConsole.systemUninstall();
+ if (this.terminalManager != null) {
+ this.terminalManager.shutdown();
+ this.terminalManager = null;
+ }
}
}
diff --git a/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/AggregateCompleter.java b/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/AggregateCompleter.java
deleted file mode 100644
index b8ee42e..0000000
--- a/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/AggregateCompleter.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * 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.felix.webconsole.plugins.gogo.impl;
-
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Collection;
-
-import jline.console.completer.Completer;
-
-/**
- * Completer which contains multipule completers and aggregates them together.
- */
-public class AggregateCompleter implements Completer
-{
- private final Collection<Completer> completers;
-
- public AggregateCompleter(final Collection<Completer> completers) {
- assert completers != null;
- this.completers = completers;
- }
-
- public int complete(final String buffer, final int cursor, final List candidates) {
- // buffer could be null
- assert candidates != null;
-
- List<Completion> completions = new ArrayList<Completion>(completers.size());
-
- // Run each completer, saving its completion results
- int max = -1;
- for (Completer completer : completers) {
- Completion completion = new Completion(candidates);
- completion.complete(completer, buffer, cursor);
-
- // Compute the max cursor position
- max = Math.max(max, completion.cursor);
-
- completions.add(completion);
- }
-
- // Append candiates from completions which have the same cursor position as max
- for (Completion completion : completions) {
- if (completion.cursor == max) {
- // noinspection unchecked
- candidates.addAll(completion.candidates);
- }
- }
-
- return max;
- }
-
- private class Completion
- {
- public final List<CharSequence> candidates;
-
- public int cursor;
-
- public Completion(final List candidates) {
- assert candidates != null;
-
- // noinspection unchecked
- this.candidates = new LinkedList<CharSequence>(candidates);
- }
-
- public void complete(final Completer completer, final String buffer, final int cursor) {
- assert completer != null;
-
- this.cursor = completer.complete(buffer, cursor, candidates);
- }
- }
-}
diff --git a/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/CloseShellException.java b/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/CloseShellException.java
deleted file mode 100644
index f3540a2..0000000
--- a/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/CloseShellException.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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.felix.webconsole.plugins.gogo.impl;
-
-
-@SuppressWarnings("serial")
-public class CloseShellException extends Exception {
-
-}
diff --git a/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/CommandSessionHolder.java b/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/CommandSessionHolder.java
deleted file mode 100644
index 0c04196..0000000
--- a/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/CommandSessionHolder.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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.felix.webconsole.plugins.gogo.impl;
-
-import org.apache.felix.service.command.CommandSession;
-
-public class CommandSessionHolder {
-
- private static final ThreadLocal<CommandSession> session = new ThreadLocal<CommandSession>();
-
- public static CommandSession getSession() {
- return session.get();
- }
-
- public static void setSession(CommandSession commandSession) {
- session.set(commandSession);
- }
-
- public static void unset() {
- session.remove();
- }
-}
diff --git a/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/CommandsCompleter.java b/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/CommandsCompleter.java
deleted file mode 100644
index 0a55a35..0000000
--- a/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/CommandsCompleter.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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.felix.webconsole.plugins.gogo.impl;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import jline.console.completer.Completer;
-
-import org.apache.felix.service.command.CommandSession;
-
-public class CommandsCompleter implements Completer {
-
- private CommandSession session;
- private final List<Completer> completers = new ArrayList<Completer>();
- private final Set<String> commands = new HashSet<String>();
-
- public CommandsCompleter(CommandSession session) {
- this.session = session;
- }
-
-
- public int complete(String buffer, int cursor, List<CharSequence> candidates) {
- checkData();
- int res = new AggregateCompleter(completers).complete(buffer, cursor, candidates);
-// TODO: Collections.sort(candidates);
- return res;
- }
-
- protected synchronized void checkData() {
- // Copy the set to avoid concurrent modification exceptions
- // TODO: fix that in gogo instead
- Set<String> names = new HashSet<String>((Set<String>) session.get(".commands" /* CommandSessionImpl.COMMANDS */));
- if (!names.equals(commands)) {
- commands.clear();
- completers.clear();
-
- // get command aliases
- Set<String> aliases = this.getAliases();
- completers.add(new StringsCompleter(aliases));
-
- // add argument completers for each command
- for (String command : names) {
- commands.add(command);
- }
- }
- }
-
- /**
- * Get the aliases defined in the console session.
- *
- * @return the aliases set
- */
- private Set<String> getAliases() {
- Set<String> vars = (Set<String>) session.get(null);
- Set<String> aliases = new HashSet<String>();
- for (String var : vars) {
- Object content = session.get(var);
- if (content != null && content.getClass().getName().equals("org.apache.felix.gogo.runtime.Closure")) {
- aliases.add(var);
- }
- }
- return aliases;
- }
-
-}
-
diff --git a/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/Console.java b/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/Console.java
index 42b3585..44c7ae1 100644
--- a/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/Console.java
+++ b/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/Console.java
@@ -18,52 +18,24 @@
*/
package org.apache.felix.webconsole.plugins.gogo.impl;
-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.PrintWriter;
-import java.io.Reader;
import java.util.Map;
-import java.util.Properties;
+import java.util.Map.Entry;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import jline.Terminal;
-import jline.UnsupportedTerminal;
-import jline.console.ConsoleReader;
-import jline.console.completer.Completer;
-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.fusesource.jansi.Ansi;
public class Console implements Runnable {
- public static final String SHELL_INIT_SCRIPT = "karaf.shell.init.script";
-
- public static final String PROMPT = "PROMPT";
-
- public static final String DEFAULT_PROMPT = "\u001B[1m${USER}\u001B[0m@${APPLICATION}> ";
-
- public static final String PRINT_STACK_TRACES = "karaf.printStackTraces";
-
- public static final String LAST_EXCEPTION = "karaf.lastException";
-
public static final String IGNORE_INTERRUPTS = "karaf.ignoreInterrupts";
protected CommandSession session;
- private ConsoleReader reader;
-
private BlockingQueue<Integer> queue;
private boolean interrupt;
@@ -76,8 +48,6 @@
private Runnable closeCallback;
- private Terminal terminal;
-
private InputStream consoleInput;
private InputStream in;
@@ -88,28 +58,20 @@
private Thread thread;
- public Console(CommandProcessor processor, InputStream in, PrintStream out, PrintStream err, Terminal term,
- Runnable closeCallback) throws Exception {
+ public Console(CommandProcessor processor, InputStream in, PrintStream out, PrintStream err,
+ Runnable closeCallback, Map<String, String> sessionProps) throws Exception {
this.in = in;
this.out = out;
this.err = err;
this.queue = new ArrayBlockingQueue<Integer>(1024);
- this.terminal = term == null ? new UnsupportedTerminal() : term;
this.consoleInput = new ConsoleInputStream();
this.session = processor.createSession(this.consoleInput, this.out, this.err);
- this.session.put("SCOPE", "shell:osgi:*");
this.closeCallback = closeCallback;
- reader = new ConsoleReader(this.consoleInput, new PrintWriter(this.out), getClass().getResourceAsStream(
- "keybinding.properties"), this.terminal);
-
- session.put(".jline.history", reader.getHistory());
- Completer completer = createCompleter();
- if (completer != null) {
- reader.addCompleter(completer);
- }
- if (Boolean.getBoolean("jline.nobell")) {
- reader.setBellEnabled(false);
+ if (sessionProps != null) {
+ for (Entry<String, String> entry: sessionProps.entrySet()) {
+ this.session.put(entry.getKey(), entry.getValue());
+ }
}
pipe = new Thread(new Pipe());
pipe.setName("gogo shell pipe thread");
@@ -121,109 +83,30 @@
}
public void close() {
- // System.err.println("Closing");
- if (reader.getHistory() instanceof PersistentHistory) {
- try {
- ((PersistentHistory) reader.getHistory()).flush();
- } catch (IOException e) {
- // ignore
- }
- }
running = false;
- CommandSessionHolder.unset();
pipe.interrupt();
}
public void run() {
- ThreadLocal<CommandSessionHolder> consoleState = new ThreadLocal<CommandSessionHolder>();
thread = Thread.currentThread();
- CommandSessionHolder.setSession(session);
running = true;
pipe.start();
- welcome();
- setSessionProperties();
- String scriptFileName = System.getProperty(SHELL_INIT_SCRIPT);
- 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) {
- System.err.println("Error in initialization script: " + e.getMessage());
- } finally {
- if (r != null) {
- try {
- r.close();
- } catch (IOException e) {
- // Ignore
- }
- }
- }
+ try
+ {
+ session.execute("gosh --login --noshutdown");
}
- while (running) {
- try {
- 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 {
- command += " " + line;
- }
- if (reader.getHistory().size() == 0) {
- reader.getHistory().add(command);
- } else {
- reader.getHistory().replace(command);
- }
- try {
- new Parser(command, 0).program();
- loop = false;
- } catch (Exception e) {
- loop = true;
- first = false;
- }
- }
- 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 (CloseShellException e) {
- break;
- } catch (Exception t) {
- try {
- session.put(LAST_EXCEPTION, t);
- session.getConsole().print(Ansi.ansi().fg(Ansi.Color.RED).toString());
- session.getConsole().println(
- "Error executing command: "
- + (t.getMessage() != null ? t.getMessage() : t.getClass().getName()));
- session.getConsole().print(Ansi.ansi().fg(Ansi.Color.DEFAULT).toString());
- } catch (Exception ignore) {
- // ignore
- }
- }
+ catch (Exception e)
+ {
+ e.printStackTrace(this.err);
+ }
+ finally
+ {
+ session.close();
+
+ this.out.println("Good Bye!");
}
close();
- // System.err.println("Exiting console...");
+
if (closeCallback != null) {
closeCallback.run();
}
@@ -243,90 +126,6 @@
return Boolean.parseBoolean(s.toString());
}
- protected void welcome() {
- Properties props = loadBrandingProperties();
- String welcome = props.getProperty("welcome");
- if (welcome != null && welcome.length() > 0) {
- session.getConsole().println(welcome);
- }
- }
-
- protected void setSessionProperties() {
- Properties props = loadBrandingProperties();
- for (Map.Entry<Object, Object> entry : props.entrySet()) {
- String key = (String) entry.getKey();
- if (key.startsWith("session.")) {
- session.put(key.substring("session.".length()), entry.getValue());
- }
- }
- }
-
- protected Completer createCompleter() {
- return new CommandsCompleter(session);
- }
-
- protected Properties loadBrandingProperties() {
- Properties props = new Properties();
- loadProps(props, "org/apache/karaf/shell/console/branding.properties");
- loadProps(props, "org/apache/karaf/branding/branding.properties");
- return props;
- }
-
- protected void loadProps(Properties props, String resource) {
- InputStream is = null;
- try {
- is = getClass().getClassLoader().getResourceAsStream(resource);
- if (is != null) {
- props.load(is);
- }
- } catch (IOException e) {
- // ignore
- } finally {
- if (is != null) {
- try {
- is.close();
- } catch (IOException e) {
- // Ignore
- }
- }
- }
- }
-
- protected String getPrompt() {
- try {
- String prompt;
- try {
- Object p = session.get(PROMPT);
- if (p != null) {
- prompt = p.toString();
- } else {
- Properties properties = loadBrandingProperties();
- 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;
@@ -408,14 +207,13 @@
try {
while (running) {
try {
- int c = terminal.readCharacter(in);
+ int c = in.read();
if (c == -1) {
return;
} else if (c == 4 && !getBoolean(IGNORE_INTERRUPTS)) {
err.println("^D");
} else if (c == 3 && !getBoolean(IGNORE_INTERRUPTS)) {
err.println("^C");
- reader.getCursorBuffer().clear();
interrupt();
}
queue.put(c);
diff --git a/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/GogoPlugin.java b/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/GogoPlugin.java
index 70b57b2..c4f12cd 100644
--- a/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/GogoPlugin.java
+++ b/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/GogoPlugin.java
@@ -22,22 +22,12 @@
package org.apache.felix.webconsole.plugins.gogo.impl;
-import java.io.ByteArrayInputStream;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.InterruptedIOException;
-import java.io.PipedInputStream;
-import java.io.PipedOutputStream;
-import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.zip.GZIPOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-
-import org.apache.felix.service.command.CommandProcessor;
-import org.apache.felix.service.command.CommandSession;
import org.apache.felix.webconsole.SimpleWebConsolePlugin;
import org.osgi.framework.BundleContext;
@@ -54,175 +44,71 @@
public static final String TITLE = "Gogo";
public static final int TERM_WIDTH = 120;
+
public static final int TERM_HEIGHT = 39;
- private CommandProcessor commandProcessor;
+ private final SessionTerminalManager terminalManager;
- public GogoPlugin() {
+ public GogoPlugin(final SessionTerminalManager terminalManager) {
super(LABEL, TITLE, null);
+ this.terminalManager = terminalManager;
}
@Override
public void activate(BundleContext bundleContext) {
super.activate(bundleContext);
- this.commandProcessor = new CommandProcessor() {
- public CommandSession createSession(InputStream in, PrintStream out, PrintStream err) {
- return ((CommandProcessor) getService(CommandProcessor.class.getName())).createSession(in, out, err);
- }
- };
}
@Override
public void deactivate() {
- this.commandProcessor = null;
super.deactivate();
}
- protected void renderContent( HttpServletRequest request, HttpServletResponse response ) throws IOException
- {
+ protected void renderContent(HttpServletRequest request, HttpServletResponse response) throws IOException {
PrintWriter pw = response.getWriter();
String appRoot = request.getContextPath() + request.getServletPath();
- pw.println( "<link href=\"" + appRoot + "/gogo/res/ui/gogo.css\" rel=\"stylesheet\" type=\"text/css\" />" );
- pw.println( "<script src=\"" + appRoot + "/gogo/res/ui/gogo.js\" type=\"text/javascript\"></script>" );
- pw.println( "<div id='console'><div id='term'></div></div>" );
- pw.println( "<script type=\"text/javascript\"><!--" );
- pw.println( "window.onload = function() { gogo.Terminal(document.getElementById(\"term\"), " + TERM_WIDTH + ", " + TERM_HEIGHT + "); }" );
- pw.println( "--></script>" );
+ pw.println("<link href=\"" + appRoot + "/gogo/res/ui/gogo.css\" rel=\"stylesheet\" type=\"text/css\" />");
+ pw.println("<script src=\"" + appRoot + "/gogo/res/ui/gogo.js\" type=\"text/javascript\"></script>");
+ pw.println("<p id=\"statline\" class=\"statline\"> </p>");
+ pw.println("<div id='console'><div id='term'></div></div>");
+ pw.println("<script type=\"text/javascript\"><!--");
+ pw.println("window.onload = function() { gogo.Terminal(document.getElementById(\"term\"), " + TERM_WIDTH + ", "
+ + TERM_HEIGHT + "); }");
+ pw.println("--></script>");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
String encoding = request.getHeader("Accept-Encoding");
boolean supportsGzip = (encoding != null && encoding.toLowerCase().indexOf("gzip") > -1);
- SessionTerminal st = getSessionTerminal(request);
- String str = request.getParameter("k");
- String f = request.getParameter("f");
- String dump = st.handle(str, f != null && f.length() > 0);
- if (dump != null) {
- if (supportsGzip) {
- response.setHeader("Content-Encoding", "gzip");
- response.setHeader("Content-Type", "text/html");
- try {
- GZIPOutputStream gzos = new GZIPOutputStream(response.getOutputStream());
- gzos.write(dump.getBytes());
- gzos.close();
- } catch (IOException ie) {
- // handle the error here
- ie.printStackTrace();
+ SessionTerminal st = terminalManager.getSessionTerminal(request);
+ if (st != null) {
+ String str = request.getParameter("k");
+ String f = request.getParameter("f");
+ String dump = st.handle(str, f != null && f.length() > 0);
+ if (dump != null) {
+ if (supportsGzip) {
+ response.setHeader("Content-Encoding", "gzip");
+ response.setHeader("Content-Type", "text/html; charset=UTF-8");
+ try {
+ GZIPOutputStream gzos = new GZIPOutputStream(response.getOutputStream());
+ gzos.write(dump.getBytes("UTF-8"));
+ gzos.close();
+ } catch (IOException ie) {
+ // handle the error here
+ ie.printStackTrace();
+ }
+ } else {
+ response.setContentType("text/html; charset=UTF-8");
+ response.getOutputStream().write(dump.getBytes("UTF-8"));
}
- } else {
- response.getOutputStream().write(dump.getBytes());
}
+ } else {
+ response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
+
+ response.flushBuffer();
}
- private SessionTerminal getSessionTerminal(final HttpServletRequest request) throws IOException{
- final Object terminal = request.getSession(true).getAttribute("terminal");
- if (terminal instanceof SessionTerminal) {
- final SessionTerminal st = (SessionTerminal) terminal;
- if (!st.isClosed()) {
- return st;
- }
- }
-
- final SessionTerminal st = new SessionTerminal(request.getRemoteUser());
- request.getSession().setAttribute("terminal", st);
- return st;
- }
-
- public class SessionTerminal implements Runnable {
-
- private Terminal terminal;
- private Console console;
- private PipedOutputStream in;
- private PipedInputStream out;
- private boolean closed;
-
- public SessionTerminal(final String user) throws IOException {
- try {
- this.terminal = new Terminal(TERM_WIDTH, TERM_HEIGHT);
- terminal.write("\u001b\u005B20\u0068"); // set newline mode on
-
- in = new PipedOutputStream();
- out = new PipedInputStream();
- PrintStream pipedOut = new PrintStream(new PipedOutputStream(out), true);
-
- console = new Console(commandProcessor,
- new PipedInputStream(in),
- pipedOut,
- pipedOut,
- new WebTerminal(TERM_WIDTH, TERM_HEIGHT),
- null);
- CommandSession session = console.getSession();
- session.put("APPLICATION", System.getProperty("karaf.name", "root"));
- session.put("USER", user);
- session.put("COLUMNS", Integer.toString(TERM_WIDTH));
- session.put("LINES", Integer.toString(TERM_HEIGHT));
- } catch (IOException e) {
- e.printStackTrace();
- throw e;
- } catch (Exception e) {
- e.printStackTrace();
- throw (IOException) new IOException().initCause(e);
- }
- new Thread(console).start();
- new Thread(this).start();
- }
-
- public boolean isClosed() {
- return closed;
- }
-
- public String handle(String str, boolean forceDump) throws IOException {
- try {
- if (str != null && str.length() > 0) {
- String d = terminal.pipe(str);
- for (byte b : d.getBytes()) {
- in.write(b);
- }
- in.flush();
- }
- } catch (IOException e) {
- closed = true;
- throw e;
- }
- try {
- return terminal.dump(10, forceDump);
- } catch (InterruptedException e) {
- throw new InterruptedIOException(e.toString());
- }
- }
-
- public void run() {
- try {
- for (;;) {
- byte[] buf = new byte[8192];
- int l = out.read(buf);
- InputStreamReader r = new InputStreamReader(new ByteArrayInputStream(buf, 0, l));
- StringBuilder sb = new StringBuilder();
- for (;;) {
- int c = r.read();
- if (c == -1) {
- break;
- }
- sb.append((char) c);
- }
- if (sb.length() > 0) {
- terminal.write(sb.toString());
- }
- String s = terminal.read();
- if (s != null && s.length() > 0) {
- for (byte b : s.getBytes()) {
- in.write(b);
- }
- }
- }
- } catch (IOException e) {
- closed = true;
- e.printStackTrace();
- }
- }
-
- }
}
diff --git a/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/NameScoping.java b/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/NameScoping.java
deleted file mode 100644
index 3d4b025..0000000
--- a/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/NameScoping.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/**
- *
- * 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.felix.webconsole.plugins.gogo.impl;
-
-import org.apache.felix.service.command.CommandSession;
-
-
-/**
- * A helper class for name scoping
- */
-public class NameScoping {
-
- public static final String MULTI_SCOPE_MODE_KEY = "MULTI_SCOPE_MODE";
-
- /**
- * Returns the name of the command which can omit the global scope prefix if the command starts with the
- * same prefix as the current application
- */
- public static String getCommandNameWithoutGlobalPrefix(CommandSession session, String key) {
- if (!isMultiScopeMode(session)) {
- String globalScope = (String) session.get("APPLICATION");
- if (globalScope != null) {
- String prefix = globalScope + ":";
- if (key.startsWith(prefix)) {
- // TODO we may only want to do this for single-scope mode when outside of OSGi?
- // so we may want to also check for a isMultiScope mode == false
- return key.substring(prefix.length());
- }
- }
- }
- return key;
- }
-
- /**
- * Returns true if the given scope is the global scope so that it can be hidden from help messages
- */
- public static boolean isGlobalScope(CommandSession session, String scope) {
- if (!isMultiScopeMode(session)) {
- String globalScope = (String) session.get("APPLICATION");
- if (globalScope != null) {
- return scope.equals(globalScope);
- }
- }
- return false;
- }
-
- /**
- * Returns true if we are in multi-scope mode (the default) or if we are in single scope mode which means we
- * avoid prefixing commands with their scope
- */
- public static boolean isMultiScopeMode(CommandSession session) {
- Object value = session.get(MULTI_SCOPE_MODE_KEY);
- if (value != null && value.equals("false")) {
- return false;
- }
- return true;
- }
-}
diff --git a/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/Parser.java b/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/Parser.java
deleted file mode 100644
index d2df531..0000000
--- a/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/Parser.java
+++ /dev/null
@@ -1,473 +0,0 @@
-/*
- * 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.
- */
-// DWB14: parser loops if // comment at start of program
-// DWB15: allow program to have trailing ';'
-package org.apache.felix.webconsole.plugins.gogo.impl;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class Parser
-{
- int current = 0;
- String text;
- boolean escaped;
- static final String SPECIAL = "<;|{[\"'$`(=";
-
- List<List<List<String>>> program;
- List<List<String>> statements;
- List<String> statement;
- int cursor;
- int start = -1;
- int c0;
- int c1;
- int c2;
- int c3;
-
- public Parser(String text, int cursor)
- {
- this.text = text;
- this.cursor = cursor;
- }
-
- void ws()
- {
- // derek: BUGFIX: loop if comment at beginning of input
- //while (!eof() && Character.isWhitespace(peek())) {
- while (!eof() && (!escaped && Character.isWhitespace(peek()) || current == 0))
- {
- if (current != 0 || !escaped && Character.isWhitespace(peek()))
- {
- current++;
- }
- if (peek() == '/' && current < text.length() - 2
- && text.charAt(current + 1) == '/')
- {
- comment();
- }
- if (current == 0)
- {
- break;
- }
- }
- }
-
- private void comment()
- {
- while (!eof() && peek() != '\n' && peek() != '\r')
- {
- next();
- }
- }
-
- boolean eof()
- {
- return current >= text.length();
- }
-
- char peek()
- {
- return peek(false);
- }
-
- char peek(boolean increment)
- {
- escaped = false;
- if (eof())
- {
- return 0;
- }
-
- int last = current;
- char c = text.charAt(current++);
-
- if (c == '\\')
- {
- escaped = true;
- if (eof())
- {
- throw new RuntimeException("Eof found after \\"); // derek
- }
-
- c = text.charAt(current++);
-
- switch (c)
- {
- case 't':
- c = '\t';
- break;
- case '\r':
- case '\n':
- c = ' ';
- break;
- case 'b':
- c = '\b';
- break;
- case 'f':
- c = '\f';
- break;
- case 'n':
- c = '\n';
- break;
- case 'r':
- c = '\r';
- break;
- case 'u':
- c = unicode();
- current += 4;
- break;
- default:
- // We just take the next character literally
- // but have the escaped flag set, important for {},[] etc
- }
- }
- if (cursor > last && cursor <= current)
- {
- c0 = program != null ? program.size() : 0;
- c1 = statements != null ? statements.size() : 0;
- c2 = statement != null ? statement.size() : 0;
- c3 = (start >= 0) ? current - start : 0;
- }
- if (!increment)
- {
- current = last;
- }
- return c;
- }
-
- public List<List<List<String>>> program()
- {
- program = new ArrayList<List<List<String>>>();
- ws();
- if (!eof())
- {
- program.add(pipeline());
- while (peek() == ';')
- {
- current++;
- List<List<String>> pipeline = pipeline();
- program.add(pipeline);
- }
- }
- if (!eof())
- {
- throw new RuntimeException("Program has trailing text: " + context(current));
- }
-
- List<List<List<String>>> p = program;
- program = null;
- return p;
- }
-
- CharSequence context(int around)
- {
- return text.subSequence(Math.max(0, current - 20), Math.min(text.length(),
- current + 4));
- }
-
- public List<List<String>> pipeline()
- {
- statements = new ArrayList<List<String>>();
- statements.add(statement());
- while (peek() == '|')
- {
- current++;
- ws();
- if (!eof())
- {
- statements.add(statement());
- }
- else
- {
- statements.add(new ArrayList<String>());
- break;
- }
- }
- List<List<String>> s = statements;
- statements = null;
- return s;
- }
-
- public List<String> statement()
- {
- statement = new ArrayList<String>();
- statement.add(value());
- while (!eof())
- {
- ws();
- if (peek() == '|' || peek() == ';')
- {
- break;
- }
-
- if (!eof())
- {
- statement.add(messy());
- }
- }
- List<String> s = statement;
- statement = null;
- return s;
- }
-
- public String messy()
- {
- start = current;
- char c = peek();
- if (c > 0 && SPECIAL.indexOf(c) < 0)
- {
- current++;
- try {
- while (!eof())
- {
- c = peek();
- if (!escaped && (c == ';' || c == '|' || Character.isWhitespace(c)))
- {
- break;
- }
- next();
- }
- return text.substring(start, current);
- } finally {
- start = -1;
- }
- }
- else
- {
- return value();
- }
- }
-
- String value()
- {
- ws();
-
- start = current;
- try {
- char c = next();
- if (!escaped)
- {
- switch (c)
- {
- case '{':
- return text.substring(start, find('}', '{'));
- case '(':
- return text.substring(start, find(')', '('));
- case '[':
- return text.substring(start, find(']', '['));
- case '<':
- return text.substring(start, find('>', '<'));
- case '=':
- return text.substring(start, current);
- case '"':
- case '\'':
- quote(c);
- break;
- }
- }
-
- // Some identifier or number
- while (!eof())
- {
- c = peek();
- if (!escaped)
- {
- if (Character.isWhitespace(c) || c == ';' || c == '|' || c == '=')
- {
- break;
- }
- else if (c == '{')
- {
- next();
- find('}', '{');
- }
- else if (c == '(')
- {
- next();
- find(')', '(');
- }
- else if (c == '<')
- {
- next();
- find('>', '<');
- }
- else if (c == '[')
- {
- next();
- find(']', '[');
- }
- else if (c == '\'' || c == '"')
- {
- next();
- quote(c);
- next();
- }
- else
- {
- next();
- }
- }
- else
- {
- next();
- }
- }
- return text.substring(start, current);
- } finally {
- start = -1;
- }
- }
-
- boolean escaped()
- {
- return escaped;
- }
-
- char next()
- {
- return peek(true);
- }
-
- char unicode()
- {
- if (current + 4 > text.length())
- {
- throw new IllegalArgumentException("Unicode \\u escape at eof at pos ..."
- + context(current) + "...");
- }
-
- String s = text.subSequence(current, current + 4).toString();
- int n = Integer.parseInt(s, 16);
- return (char) n;
- }
-
- int find(char target, char deeper)
- {
- int start = current;
- int level = 1;
-
- while (level != 0)
- {
- if (eof())
- {
- throw new RuntimeException("Eof found in the middle of a compound for '"
- + target + deeper + "', begins at " + context(start));
- }
-
- char c = next();
- if (!escaped)
- {
- if (c == target)
- {
- level--;
- }
- else
- {
- if (c == deeper)
- {
- level++;
- }
- else
- {
- if (c == '"')
- {
- quote('"');
- }
- else
- {
- if (c == '\'')
- {
- quote('\'');
- }
- else
- {
- if (c == '`')
- {
- quote('`');
- }
- }
- }
- }
- }
- }
- }
- return current;
- }
-
- int quote(char which)
- {
- while (!eof() && (peek() != which || escaped))
- {
- next();
- }
-
- return current++;
- }
-
- CharSequence findVar()
- {
- int start = current;
- char c = peek();
-
- if (c == '{')
- {
- next();
- int end = find('}', '{');
- return text.subSequence(start, end);
- }
- if (c == '(')
- {
- next();
- int end = find(')', '(');
- return text.subSequence(start, end);
- }
-
- if (Character.isJavaIdentifierPart(c))
- {
- while (c == '$')
- {
- c = next();
- }
- while (!eof() && (Character.isJavaIdentifierPart(c) || c == '.') && c != '$')
- {
- next();
- c = peek();
- }
- return text.subSequence(start, current);
- }
- throw new IllegalArgumentException(
- "Reference to variable does not match syntax of a variable: "
- + context(start));
- }
-
- public String toString()
- {
- return "..." + context(current) + "...";
- }
-
- public String unescape()
- {
- StringBuilder sb = new StringBuilder();
- while (!eof())
- {
- sb.append(next());
- }
- return sb.toString();
- }
-}
diff --git a/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/SessionTerminal.java b/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/SessionTerminal.java
new file mode 100644
index 0000000..6a9baff
--- /dev/null
+++ b/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/SessionTerminal.java
@@ -0,0 +1,143 @@
+/*
+ * 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.felix.webconsole.plugins.gogo.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.InterruptedIOException;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.io.PrintStream;
+import java.util.HashMap;
+
+import org.apache.felix.service.command.CommandProcessor;
+
+public class SessionTerminal implements Runnable {
+
+ private Terminal terminal;
+ private Console console;
+ private PipedOutputStream in;
+ private PipedInputStream out;
+ private boolean closed;
+
+ @SuppressWarnings("serial")
+ public SessionTerminal(final CommandProcessor commandProcessor, final String user) throws IOException {
+ try {
+ this.terminal = new Terminal(GogoPlugin.TERM_WIDTH, GogoPlugin.TERM_HEIGHT);
+ terminal.write("\u001b\u005B20\u0068"); // set newline mode on
+
+ in = new PipedOutputStream();
+ out = new PipedInputStream();
+ PrintStream pipedOut = new PrintStream(new PipedOutputStream(out), true);
+
+ console = new Console(commandProcessor, new PipedInputStream(in), pipedOut, pipedOut, new Runnable() {
+ public void run() {
+ SessionTerminal.this.terminal.write("done...");
+ close();
+ }
+ }, new HashMap<String, String>() {
+ {
+ put("USER", user);
+ put("COLUMNS", Integer.toString(GogoPlugin.TERM_WIDTH));
+ put("LINES", Integer.toString(GogoPlugin.TERM_HEIGHT));
+ }
+ });
+ } catch (IOException e) {
+ throw e;
+ } catch (Exception e) {
+ throw (IOException) new IOException().initCause(e);
+ }
+ new Thread(console).start();
+ new Thread(this).start();
+ }
+
+ public boolean isClosed() {
+ return closed;
+ }
+
+ public void close() {
+ if (!closed) {
+ this.closed = true;
+
+ this.console.close();
+
+ try {
+ this.in.close();
+ } catch (IOException e) {
+ }
+ try {
+ this.out.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ public String handle(String str, boolean forceDump) throws IOException {
+ try {
+ if (str != null && str.length() > 0) {
+ String d = terminal.pipe(str);
+ // echo unless backspace
+ if (d.charAt(0) != '\b') {
+ terminal.write(d);
+ }
+ in.write(d.getBytes());
+ in.flush();
+ }
+ } catch (IOException e) {
+ // ignore
+ }
+ try {
+ return terminal.dump(10, forceDump);
+ } catch (InterruptedException e) {
+ throw new InterruptedIOException(e.toString());
+ }
+ }
+
+ public void run() {
+ try {
+ for (;;) {
+ byte[] buf = new byte[8192];
+ int l = out.read(buf);
+ InputStreamReader r = new InputStreamReader(new ByteArrayInputStream(buf, 0, l));
+ StringBuilder sb = new StringBuilder();
+ for (;;) {
+ int c = r.read();
+ if (c == -1) {
+ break;
+ }
+ sb.append((char) c);
+ }
+ if (sb.length() > 0) {
+ terminal.write(sb.toString());
+ }
+ String s = terminal.read();
+ if (s != null && s.length() > 0) {
+ for (byte b : s.getBytes()) {
+ in.write(b);
+ }
+ }
+ }
+ } catch (IOException e) {
+ closed = true;
+ e.printStackTrace();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/SessionTerminalManager.java b/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/SessionTerminalManager.java
new file mode 100644
index 0000000..a539ed5
--- /dev/null
+++ b/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/SessionTerminalManager.java
@@ -0,0 +1,114 @@
+/*
+ * 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.felix.webconsole.plugins.gogo.impl;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
+
+import org.apache.felix.service.command.CommandProcessor;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * The <code>SessionTerminalManager</code> manages {@link SessionTerminal}
+ * instances on behalf of the {@link GogoPlugin}. The instances are stored in
+ * HttpSessions and cleared when (a) the owning session is destroyed and (b)
+ * when this manager is {@link #shutdown() shut down}.
+ */
+public class SessionTerminalManager implements HttpSessionListener {
+
+ private final ServiceRegistration service;
+
+ private final ServiceTracker commandProcessor;
+
+ private final Set<SessionTerminal> sessions = new HashSet<SessionTerminal>();
+
+ @SuppressWarnings("serial")
+ public SessionTerminalManager(final BundleContext context) {
+ this.commandProcessor = new ServiceTracker(context, CommandProcessor.class.getName(), null) {
+ @Override
+ public void removedService(ServiceReference reference, Object service) {
+ cleanupSessions();
+ super.removedService(reference, service);
+ }
+ };
+ this.commandProcessor.open();
+
+ Hashtable<String, Object> props = new Hashtable<String, Object>();
+ props.put(Constants.SERVICE_DESCRIPTION, "Gogo Shell Terminal Session Reaper");
+ service = context.registerService(HttpSessionListener.class.getName(), this, props);
+ }
+
+ void shutdown() {
+ this.service.unregister();
+ this.commandProcessor.close();
+ this.cleanupSessions();
+ }
+
+ SessionTerminal getSessionTerminal(final HttpServletRequest request) throws IOException {
+ final HttpSession session = request.getSession(true);
+ final Object terminal = session.getAttribute("terminal");
+ if (terminal instanceof SessionTerminal) {
+ final SessionTerminal st = (SessionTerminal) terminal;
+ if (!st.isClosed()) {
+ return st;
+ }
+ }
+
+ final CommandProcessor cp = (CommandProcessor) this.commandProcessor.getService();
+ if (cp != null) {
+ final SessionTerminal st = new SessionTerminal(cp, request.getRemoteUser());
+ this.sessions.add(st);
+ session.setAttribute("terminal", st);
+ return st;
+ }
+
+ // no session because there is no command processor !
+ return null;
+ }
+
+ public void sessionCreated(HttpSessionEvent event) {
+ // don't care
+ }
+
+ public void sessionDestroyed(HttpSessionEvent event) {
+ final Object terminal = event.getSession().getAttribute("terminal");
+ if (terminal instanceof SessionTerminal) {
+ sessions.remove(terminal);
+ ((SessionTerminal) terminal).close();
+ }
+ }
+
+ void cleanupSessions() {
+ for (SessionTerminal session : sessions) {
+ session.close();
+ }
+ this.sessions.clear();
+ }
+}
diff --git a/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/StringsCompleter.java b/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/StringsCompleter.java
deleted file mode 100644
index c551db2..0000000
--- a/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/StringsCompleter.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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.felix.webconsole.plugins.gogo.impl;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.SortedSet;
-import java.util.TreeSet;
-
-import jline.console.completer.Completer;
-
-/**
- * Completer for a set of strings.
- */
-public class StringsCompleter implements Completer {
-
- private final SortedSet<String> strings = new TreeSet<String>();
-
- private final boolean caseSensitive;
-
- public StringsCompleter() {
- this(true);
- }
-
- public StringsCompleter(final boolean caseSensitive) {
- this.caseSensitive = caseSensitive;
- }
-
- public StringsCompleter(final Collection<String> strings) {
- this();
- assert strings != null;
- getStrings().addAll(strings);
- }
-
- public StringsCompleter(final String[] strings, boolean caseSensitive) {
- this(Arrays.asList(strings), caseSensitive);
- }
-
- public StringsCompleter(final Collection<String> strings, boolean caseSensitive) {
- this(caseSensitive);
- assert strings != null;
- getStrings().addAll(strings);
- }
-
- public StringsCompleter(final String[] strings) {
- this(Arrays.asList(strings));
- }
-
- public SortedSet<String> getStrings() {
- return strings;
- }
-
- public int complete(String buffer, final int cursor, final List candidates) {
- // buffer could be null
- assert candidates != null;
-
- if (buffer == null) {
- buffer = "";
- }
- if (!caseSensitive) {
- buffer = buffer.toLowerCase();
- }
-
- // KARAF-421, use getStrings() instead strings field.
- SortedSet<String> matches = getStrings().tailSet(buffer);
-
- for (String match : matches) {
- String s = caseSensitive ? match : match.toLowerCase();
- if (!s.startsWith(buffer)) {
- break;
- }
-
- // noinspection unchecked
- candidates.add(match);
- }
-
- if (candidates.size() == 1) {
- // noinspection unchecked
- candidates.set(0, candidates.get(0) + " ");
- }
-
- return candidates.isEmpty() ? -1 : 0;
- }
-}
diff --git a/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/WebTerminal.java b/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/WebTerminal.java
deleted file mode 100644
index b47553e..0000000
--- a/webconsole-plugins/gogo/src/main/java/org/apache/felix/webconsole/plugins/gogo/impl/WebTerminal.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.felix.webconsole.plugins.gogo.impl;
-
-import jline.TerminalSupport;
-
-public class WebTerminal extends TerminalSupport {
-
- private int width;
- private int height;
-
- public WebTerminal(int width, int height) {
- super(true);
- this.width = width;
- this.height = height;
- }
-
- public void init() throws Exception {
- }
-
- public void restore() throws Exception {
- }
-
- public int getWidth() {
- return width;
- }
-
- public int getHeight() {
- return height;
- }
-
-}
diff --git a/webconsole-plugins/gogo/src/main/resources/res/ui/gogo.js b/webconsole-plugins/gogo/src/main/resources/res/ui/gogo.js
index 77044a7..a204d48 100644
--- a/webconsole-plugins/gogo/src/main/resources/res/ui/gogo.js
+++ b/webconsole-plugins/gogo/src/main/resources/res/ui/gogo.js
@@ -36,7 +36,7 @@
var dstat = document.createElement('pre');
var sled = document.createElement('span');
- var sdebug = document.createElement('span');
+ var sdebug = document.getElementById('statline');
var dterm = document.createElement('div');
function debug(s) {
@@ -45,11 +45,11 @@
function error() {
sled.className = 'off';
- debug("Connection lost timeout ts:" + ((new Date).getTime()));
+ debug("Gogo Shell Processor not available");
}
function update() {
- if (sending == 0) {
+ if (sending == 0 /* && keybuf.length > 0 */ ) {
sending = 1;
sled.className = 'on';
var r = new XMLHttpRequest();
@@ -79,8 +79,10 @@
sending=0;
sled.className = 'off';
timeout = window.setTimeout(update, rmax);
+ } else if (r.status == 500) {
+ debug("Gogo Shell Processor not available")
} else {
- debug("Connection error status:" + r.status);
+ debug("General failure with Gogo Shell: " + r.status);
}
}
}
@@ -183,11 +185,13 @@
default: k = String.fromCharCode(kc); break;
}
+ var s = encodeURIComponent(k);
+
// debug("fromkeydown=" + fromkeydown + ", ev.keyCode=" + ev.keyCode + ", " +
// "ev.which=" + ev.which + ", ev.ctrlKey=" + ev.ctrlKey + ", " +
-// "kc=" + kc + ", k=" + k);
+// "kc=" + kc + ", k=" + k + ", s=" + s);
- queue(encodeURIComponent(k));
+ queue(s);
ev.cancelBubble = true;
if (ev.stopPropagation) ev.stopPropagation();
@@ -203,8 +207,22 @@
113:1, 114:1, 115:1, 116:1, 117:1, 118:1, 119:1, 120:1, 121:1, 122:1, 123:1 };
if (o[ev.keyCode] || ev.ctrlKey || ev.altKey) {
keypress(ev, true);
+ } else {
+ ev.cancelBubble = true;
+ if (ev.stopPropagation) ev.stopPropagation();
+ if (ev.preventDefault) ev.preventDefault();
}
}
+
+ function ignoreKey(ev) {
+ if (!ev) {
+ ev = window.event;
+ }
+
+ ev.cancelBubble = true;
+ if (ev.stopPropagation) ev.stopPropagation();
+ if (ev.preventDefault) ev.preventDefault();
+ }
function init() {
if (typeof(XMLHttpRequest) == "undefined") {
@@ -220,18 +238,22 @@
throw new Error("This browser does not support XMLHttpRequest.");
};
}
+
sled.appendChild(document.createTextNode('\xb7'));
sled.className = 'off';
dstat.appendChild(sled);
dstat.appendChild(document.createTextNode(' '));
- dstat.appendChild(sdebug);
+// dstat.appendChild(sdebug);
dstat.className = 'stat';
+
div.appendChild(dstat);
var d = document.createElement('div');
d.appendChild(dterm);
div.appendChild(d);
+
document.onkeypress = keypress;
- document.onkeydown = keydown;
+ document.onkeydown = ignoreKey;
+ document.onkeyup = ignoreKey;
timeout = window.setTimeout(update, 100);
}