Refactor multiple CommandProcessors support.
diff --git a/console/org.eclipse.equinox.console.supportability.tests/src/org/eclipse/equinox/console/ssh/SshCommandTests.java b/console/org.eclipse.equinox.console.supportability.tests/src/org/eclipse/equinox/console/ssh/SshCommandTests.java
index 76212e1..7fab978 100644
--- a/console/org.eclipse.equinox.console.supportability.tests/src/org/eclipse/equinox/console/ssh/SshCommandTests.java
+++ b/console/org.eclipse.equinox.console.supportability.tests/src/org/eclipse/equinox/console/ssh/SshCommandTests.java
@@ -73,7 +73,7 @@
CommandSession session = EasyMock.createMock(CommandSession.class);
EasyMock.makeThreadSafe(session, true);
session.put((String)EasyMock.anyObject(), EasyMock.anyObject());
- EasyMock.expectLastCall().times(4);
+ EasyMock.expectLastCall().times(5);
EasyMock.expect(session.execute(GOGO_SHELL_COMMAND)).andReturn(null);
session.close();
EasyMock.expectLastCall();
diff --git a/console/org.eclipse.equinox.console.supportability.tests/src/org/eclipse/equinox/console/ssh/SshCommandWithConfigAdminTests.java b/console/org.eclipse.equinox.console.supportability.tests/src/org/eclipse/equinox/console/ssh/SshCommandWithConfigAdminTests.java
index 09a585b..099442d 100644
--- a/console/org.eclipse.equinox.console.supportability.tests/src/org/eclipse/equinox/console/ssh/SshCommandWithConfigAdminTests.java
+++ b/console/org.eclipse.equinox.console.supportability.tests/src/org/eclipse/equinox/console/ssh/SshCommandWithConfigAdminTests.java
@@ -77,7 +77,7 @@
CommandSession session = EasyMock.createMock(CommandSession.class);
EasyMock.makeThreadSafe(session, true);
session.put((String)EasyMock.anyObject(), EasyMock.anyObject());
- EasyMock.expectLastCall().times(4);
+ EasyMock.expectLastCall().times(5);
EasyMock.expect(session.execute(GOGO_SHELL_COMMAND)).andReturn(null);
session.close();
EasyMock.expectLastCall();
diff --git a/console/org.eclipse.equinox.console.supportability.tests/src/org/eclipse/equinox/console/ssh/SshDisconnectCommand.java b/console/org.eclipse.equinox.console.supportability.tests/src/org/eclipse/equinox/console/ssh/SshDisconnectCommandTests.java
similarity index 95%
rename from console/org.eclipse.equinox.console.supportability.tests/src/org/eclipse/equinox/console/ssh/SshDisconnectCommand.java
rename to console/org.eclipse.equinox.console.supportability.tests/src/org/eclipse/equinox/console/ssh/SshDisconnectCommandTests.java
index 91eb454..8ab10b2 100644
--- a/console/org.eclipse.equinox.console.supportability.tests/src/org/eclipse/equinox/console/ssh/SshDisconnectCommand.java
+++ b/console/org.eclipse.equinox.console.supportability.tests/src/org/eclipse/equinox/console/ssh/SshDisconnectCommandTests.java
@@ -35,7 +35,7 @@
import org.junit.Test;
import org.osgi.framework.BundleContext;
-public class SshDisconnectCommand {
+public class SshDisconnectCommandTests {
private static final int TEST_CONTENT = 100;
private static final String USER_STORE_FILE_NAME = "org.eclipse.equinox.console.jaas.file";
private static final String JAAS_CONFIG_FILE_NAME = "jaas.config";
@@ -56,7 +56,7 @@
private static final String HOST = "localhost";
private static final int SSH_PORT = 2222;
private static final long WAIT_TIME = 5000;
- private SshShell sshShell;
+ private SshSession sshSession;
private InputStream in;
@Before
@@ -77,19 +77,21 @@
session.put((String)EasyMock.anyObject(), EasyMock.anyObject());
EasyMock.expectLastCall();
session.put((String)EasyMock.anyObject(), EasyMock.anyObject());
+ EasyMock.expectLastCall();
+ session.put((String)EasyMock.anyObject(), EasyMock.anyObject());
EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
@Override
public Object answer() throws Throwable {
- sshShell = (SshShell)EasyMock.getCurrentArguments()[1];
+ sshSession = (SshSession)EasyMock.getCurrentArguments()[1];
return null;
}
});
EasyMock.expect(session.execute(GOGO_SHELL_COMMAND)).andReturn(null);
- EasyMock.expect(session.get("CLOSEABLE")).andReturn(sshShell);
+ EasyMock.expect(session.get("CLOSEABLE")).andReturn(sshSession);
session.close();
- EasyMock.expectLastCall();
+ EasyMock.expectLastCall().atLeastOnce();
EasyMock.replay(session);
CommandProcessor processor = EasyMock.createMock(CommandProcessor.class);
diff --git a/console/org.eclipse.equinox.console.supportability.tests/src/org/eclipse/equinox/console/ssh/SshShellTests.java b/console/org.eclipse.equinox.console.supportability.tests/src/org/eclipse/equinox/console/ssh/SshShellTests.java
index 69a8642..9c041fd 100644
--- a/console/org.eclipse.equinox.console.supportability.tests/src/org/eclipse/equinox/console/ssh/SshShellTests.java
+++ b/console/org.eclipse.equinox.console.supportability.tests/src/org/eclipse/equinox/console/ssh/SshShellTests.java
@@ -17,7 +17,9 @@
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import org.apache.felix.service.command.CommandProcessor;
@@ -93,7 +95,9 @@
EasyMock.expect(env.getEnv()).andReturn(environment);
EasyMock.replay(env);
- shell = new SshShell(processor, context);
+ List<CommandProcessor> processors = new ArrayList<CommandProcessor>();
+ processors.add(processor);
+ shell = new SshShell(processors, context);
shell.setInputStream(socketServer.getInputStream());
shell.setOutputStream(socketServer.getOutputStream());
shell.start(env);
diff --git a/console/org.eclipse.equinox.console.supportability.tests/src/org/eclipse/equinox/console/telnet/TelnetServerTests.java b/console/org.eclipse.equinox.console.supportability.tests/src/org/eclipse/equinox/console/telnet/TelnetServerTests.java
index 319912e..21eb4c5 100644
--- a/console/org.eclipse.equinox.console.supportability.tests/src/org/eclipse/equinox/console/telnet/TelnetServerTests.java
+++ b/console/org.eclipse.equinox.console.supportability.tests/src/org/eclipse/equinox/console/telnet/TelnetServerTests.java
@@ -22,6 +22,8 @@
import java.io.PrintStream;
import java.net.ConnectException;
import java.net.Socket;
+import java.util.ArrayList;
+import java.util.List;
import static org.easymock.EasyMock.*;
@@ -47,7 +49,9 @@
EasyMock.expect(processor.createSession((ConsoleInputStream)EasyMock.anyObject(), (PrintStream)EasyMock.anyObject(), (PrintStream)EasyMock.anyObject())).andReturn(session);
EasyMock.replay(processor);
- TelnetServer telnetServer = new TelnetServer(null, processor, HOST, PORT);
+ List<CommandProcessor> processors = new ArrayList<CommandProcessor>();
+ processors.add(processor);
+ TelnetServer telnetServer = new TelnetServer(null, processors, HOST, PORT);
telnetServer.start();
Socket socketClient = null;
@@ -88,7 +92,9 @@
EasyMock.expect(processor.createSession((ConsoleInputStream)EasyMock.anyObject(), (PrintStream)EasyMock.anyObject(), (PrintStream)EasyMock.anyObject())).andReturn(session);
EasyMock.replay(processor);
- TelnetServer telnetServer = new TelnetServer(null, processor, null, PORT);
+ List<CommandProcessor> processors = new ArrayList<CommandProcessor>();
+ processors.add(processor);
+ TelnetServer telnetServer = new TelnetServer(null, processors, null, PORT);
telnetServer.start();
Socket socketClient = null;
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/command/adapter/Activator.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/command/adapter/Activator.java
index 56aac3b..d6a4b4c 100644
--- a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/command/adapter/Activator.java
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/command/adapter/Activator.java
@@ -55,6 +55,11 @@
private ServiceTracker<PermissionAdmin, ?> permissionAdminTracker;
private ServiceTracker<PackageAdmin, PackageAdmin> packageAdminTracker;
private ServiceTracker<PlatformAdmin, ?> platformAdminTracker;
+ private static boolean isFirstProcessor = true;
+ private static TelnetCommand telnetConnection = null;
+ private static SshCommand sshConnection = null;
+ private static Object telnetLock = new Object();
+ private static Object sshLock = new Object();
private static List<TelnetCommand> telnetConnections = new ArrayList<TelnetCommand>();
private static List<SshCommand> sshConnections = new ArrayList<SshCommand>();
@@ -79,12 +84,16 @@
if (processor == null)
return null;
- TelnetCommand telnetCommand = new TelnetCommand(processor, context);
- telnetCommand.start();
- telnetConnections.add(telnetCommand);
- SshCommand sshCommand = new SshCommand(processor, context);
- sshCommand.start();
- sshConnections.add(sshCommand);
+ if (isFirstProcessor) {
+ isFirstProcessor = false;
+ telnetConnection = new TelnetCommand(processor, context);
+ telnetConnection.start();
+ sshConnection = new SshCommand(processor, context);
+ sshConnection.start();
+ } else {
+ telnetConnection.addCommandProcessor(processor);
+ sshConnection.addCommandProcessor(processor);
+ }
ServiceTracker<ConsoleSession, CommandSession> tracker = new ServiceTracker<ConsoleSession, CommandSession>(context, ConsoleSession.class, new SessionCustomizer(context, processor));
tracker.open();
@@ -101,6 +110,9 @@
ServiceReference<CommandProcessor> reference,
ServiceTracker<ConsoleSession, CommandSession> tracker) {
tracker.close();
+ CommandProcessor processor = context.getService(reference);
+ telnetConnection.removeCommandProcessor(processor);
+ sshConnection.removeCommandProcessor(processor);
}
}
@@ -315,21 +327,18 @@
if (equinoxCmdProvider != null) {
equinoxCmdProvider.stop();
}
-
- for (TelnetCommand telnetCommand : telnetConnections) {
- try {
- telnetCommand.telnet(new String[]{"stop"});
- } catch (Exception e) {
- // expected if the telnet server is not started
- }
+
+ try {
+ telnetConnection.telnet(new String[]{"stop"});
+ } catch (Exception e) {
+ // expected if the telnet server is not started
}
-
- for (SshCommand sshCommand : sshConnections) {
- try {
- sshCommand.ssh(new String[]{"stop"});
- } catch (Exception e) {
- // expected if the ssh server is not started
- }
+
+ try {
+ sshConnection.ssh(new String[]{"stop"});
+ } catch (Exception e) {
+ // expected if the ssh server is not started
}
+
}
}
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/SshCommand.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/SshCommand.java
index 2f4d189..136509a 100644
--- a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/SshCommand.java
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/SshCommand.java
@@ -14,8 +14,10 @@
import java.io.IOException;
import java.net.BindException;
import java.net.ServerSocket;
+import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Hashtable;
+import java.util.List;
import org.apache.felix.service.command.CommandProcessor;
import org.apache.felix.service.command.Descriptor;
@@ -34,7 +36,7 @@
public class SshCommand {
private String defaultHost = null;
private int defaultPort;
- private final CommandProcessor processor;
+ private List<CommandProcessor> processors = new ArrayList<CommandProcessor>();
private String host = null;
private int port;
private SshServ sshServ;
@@ -53,7 +55,7 @@
private static final String ENABLED = "enabled";
public SshCommand(CommandProcessor processor, BundleContext context) {
- this.processor = processor;
+ processors.add(processor);
this.context = context;
if ("true".equals(context.getProperty(USE_CONFIG_ADMIN_PROP))) {
@@ -160,7 +162,7 @@
checkPortAvailable(port);
try {
- sshServ = new SshServ(processor, context, host, port);
+ sshServ = new SshServ(processors, context, host, port);
} catch (NoClassDefFoundError e) {
// ssh server bundles are optional and may not be available
System.out.println("SSH bundles not available! If you want to use SSH, please install Apache sshd-core, Apache mina-core, slf4j-api and a slf4j logger implementation bundles");
@@ -200,6 +202,16 @@
}
}
+ public synchronized void addCommandProcessor(CommandProcessor processor) {
+ processors.add(processor);
+ sshServ.addCommandProcessor(processor);
+ }
+
+ public synchronized void removeCommandProcessor(CommandProcessor processor) {
+ processors.remove(processor);
+ sshServ.removeCommandProcessor(processor);
+ }
+
private void checkPortAvailable(int port) throws Exception {
ServerSocket socket = null;
try {
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/SshServ.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/SshServ.java
index 1f10179..a8cfed6 100644
--- a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/SshServ.java
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/SshServ.java
@@ -12,6 +12,7 @@
package org.eclipse.equinox.console.ssh;
import java.io.IOException;
+import java.util.List;
import org.apache.felix.service.command.CommandProcessor;
import org.apache.sshd.SshServer;
@@ -34,10 +35,10 @@
private static final String SSH_KEYSTORE_PROP_DEFAULT = "hostkey.ser";
private static final String EQUINOX_CONSOLE_DOMAIN = "equinox_console";
- public SshServ(CommandProcessor processor, BundleContext context, String host, int port) {
+ public SshServ(List<CommandProcessor> processors, BundleContext context, String host, int port) {
this.host = host;
this.port = port;
- shellFactory = new SshShellFactory(processor, context);
+ shellFactory = new SshShellFactory(processors, context);
}
public void run() throws RuntimeException {
@@ -69,6 +70,14 @@
}
}
+ public synchronized void addCommandProcessor(CommandProcessor processor) {
+ shellFactory.addCommandProcessor(processor);
+ }
+
+ public synchronized void removeCommandProcessor(CommandProcessor processor) {
+ shellFactory.removeCommandProcessor(processor);
+ }
+
private PasswordAuthenticator createJaasPasswordAuthenticator() {
JaasPasswordAuthenticator jaasPasswordAuthenticator = new JaasPasswordAuthenticator();
jaasPasswordAuthenticator.setDomain(EQUINOX_CONSOLE_DOMAIN);
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/SshSession.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/SshSession.java
new file mode 100755
index 0000000..38a67db
--- /dev/null
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/SshSession.java
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Lazar Kirchev, SAP AG - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.console.ssh;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.Map;
+
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.CommandSession;
+import org.eclipse.equinox.console.common.ConsoleInputStream;
+import org.eclipse.equinox.console.common.ConsoleOutputStream;
+import org.eclipse.equinox.console.common.KEYS;
+import org.eclipse.equinox.console.common.terminal.TerminalTypeMappings;
+import org.eclipse.equinox.console.storage.SecureUserStore;
+import org.eclipse.equinox.console.supportability.ConsoleInputHandler;
+import org.eclipse.equinox.console.supportability.ConsoleInputScanner;
+import org.osgi.framework.BundleContext;
+
+/**
+ * This class manages a ssh connection. It is responsible for wrapping the original io streams
+ * from the socket, and starting a CommandSession to execute commands from the ssh.
+ *
+ */
+public class SshSession extends Thread implements Closeable {
+ private CommandProcessor processor;
+ private BundleContext context;
+ private SshShell sshShell;
+ private InputStream in;
+ private OutputStream out;
+ private TerminalTypeMappings currentMappings;
+ private Map<String, KEYS> currentEscapesToKey;
+
+ private static final String PROMPT = "prompt";
+ private static final String OSGI_PROMPT = "osgi> ";
+ private static final String SCOPE = "SCOPE";
+ private static final String EQUINOX_SCOPE = "equinox:*";
+ private static final String INPUT_SCANNER = "INPUT_SCANNER";
+ private static final String SSH_INPUT_SCANNER = "SSH_INPUT_SCANNER";
+ private static final String USER_STORAGE_PROPERTY_NAME = "osgi.console.ssh.useDefaultSecureStorage";
+ private static final String DEFAULT_USER = "equinox";
+ private static final String CLOSEABLE = "CLOSEABLE";
+ private static final int ADD_USER_COUNTER_LIMIT = 2;
+
+ public SshSession(CommandProcessor processor, BundleContext context, SshShell sshShell, InputStream in, OutputStream out, TerminalTypeMappings currentMappings, Map<String, KEYS> currentExcapesToKey) {
+ this.processor = processor;
+ this.context = context;
+ this.sshShell = sshShell;
+ this.in = in;
+ this.out = out;
+ this.currentMappings = currentMappings;
+ this.currentEscapesToKey = currentExcapesToKey;
+ }
+
+ public void run() {
+ ConsoleInputStream input = new ConsoleInputStream();
+ ConsoleOutputStream outp = new ConsoleOutputStream(out);
+ SshInputHandler inputHandler = new SshInputHandler(in, input, outp);
+ inputHandler.getScanner().setBackspace(currentMappings.getBackspace());
+ inputHandler.getScanner().setDel(currentMappings.getDel());
+ inputHandler.getScanner().setCurrentEscapesToKey(currentEscapesToKey);
+ inputHandler.getScanner().setEscapes(currentMappings.getEscapes());
+ inputHandler.start();
+
+ ConsoleInputStream inp = new ConsoleInputStream();
+ ConsoleInputHandler consoleInputHandler = new ConsoleInputHandler(input, inp, outp);
+ consoleInputHandler.getScanner().setBackspace(currentMappings.getBackspace());
+ consoleInputHandler.getScanner().setDel(currentMappings.getDel());
+ consoleInputHandler.getScanner().setCurrentEscapesToKey(currentEscapesToKey);
+ consoleInputHandler.getScanner().setEscapes(currentMappings.getEscapes());
+ ((ConsoleInputScanner)consoleInputHandler.getScanner()).setContext(context);
+ consoleInputHandler.start();
+
+ final CommandSession session;
+ final PrintStream output = new PrintStream(outp);
+
+ session = processor.createSession(inp, output, output);
+ session.put(SCOPE, EQUINOX_SCOPE);
+ session.put(PROMPT, OSGI_PROMPT);
+ session.put(INPUT_SCANNER, consoleInputHandler.getScanner());
+ session.put(SSH_INPUT_SCANNER, inputHandler.getScanner());
+ // Store this closeable object in the session, so that the disconnect command can close it
+ session.put(CLOSEABLE, this);
+ ((ConsoleInputScanner)consoleInputHandler.getScanner()).setSession(session);
+
+ try {
+ if ("true".equals(context.getProperty(USER_STORAGE_PROPERTY_NAME))) {
+ String[] names = SecureUserStore.getUserNames();
+ for (String name : names) {
+ // if the default user is the only user, request creation of a new user and delete the default
+ if (DEFAULT_USER.equals(name)) {
+ if (names.length == 1) {
+ session.getConsole().println("Currently the default user is the only one; since it will be deleted after first login, create a new user:");
+ boolean isUserAdded =false;
+ int count = 0;
+ while (!isUserAdded && count < ADD_USER_COUNTER_LIMIT ){
+ isUserAdded = ((Boolean) session.execute("addUser")).booleanValue();
+ count++;
+ }
+ if (!isUserAdded) {
+ break;
+ }
+ }
+ if (SecureUserStore.existsUser(name)) {
+ SecureUserStore.deleteUser(name);
+ }
+ break;
+ }
+ }
+ }
+ session.execute("gosh --login --noshutdown");
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ session.close();
+ }
+
+ }
+
+ public void close() throws IOException {
+ this.interrupt();
+ sshShell.removeSession(this);
+ }
+
+}
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/SshShell.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/SshShell.java
index 99992a1..f1f4839 100644
--- a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/SshShell.java
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/SshShell.java
@@ -15,18 +15,14 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.io.PrintStream;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import org.apache.felix.service.command.CommandProcessor;
-import org.apache.felix.service.command.CommandSession;
import org.apache.sshd.server.Command;
import org.apache.sshd.server.Environment;
import org.apache.sshd.server.ExitCallback;
-import java.io.Closeable;
-import org.eclipse.equinox.console.common.ConsoleInputStream;
-import org.eclipse.equinox.console.common.ConsoleOutputStream;
import org.eclipse.equinox.console.common.KEYS;
import org.eclipse.equinox.console.common.terminal.ANSITerminalTypeMappings;
import org.eclipse.equinox.console.common.terminal.SCOTerminalTypeMappings;
@@ -34,43 +30,30 @@
import org.eclipse.equinox.console.common.terminal.VT100TerminalTypeMappings;
import org.eclipse.equinox.console.common.terminal.VT220TerminalTypeMappings;
import org.eclipse.equinox.console.common.terminal.VT320TerminalTypeMappings;
-import org.eclipse.equinox.console.storage.SecureUserStore;
-import org.eclipse.equinox.console.supportability.ConsoleInputHandler;
-import org.eclipse.equinox.console.supportability.ConsoleInputScanner;
import org.osgi.framework.BundleContext;
/**
- * This class manages a ssh connection. It is responsible for wrapping the original io streams
- * from the ssh server, and starting a CommandSession to execute commands from the ssh.
+ * This class manages a ssh connection. It is responsible for starting a sessions to execute commands
+ * from the ssh. If there are multiple CommandProcessors, a session is started for each of them.
*
*/
-public class SshShell implements Command, Closeable {
+public class SshShell implements Command {
- private CommandProcessor processor;
+ private List<CommandProcessor> processors;
private BundleContext context;
private InputStream in;
private OutputStream out;
private ExitCallback callback;
- private Thread thread;
+ private Map<CommandProcessor, SshSession> commandProcessorToConsoleThreadMap = new HashMap<CommandProcessor, SshSession>();
private final Map<String, TerminalTypeMappings> supportedEscapeSequences;
private static final String DEFAULT_TTYPE = File.separatorChar == '/' ? "XTERM" : "ANSI";
private TerminalTypeMappings currentMappings;
private Map<String, KEYS> currentEscapesToKey;
- private static final String PROMPT = "prompt";
- private static final String OSGI_PROMPT = "osgi> ";
- private static final String SCOPE = "SCOPE";
- private static final String EQUINOX_SCOPE = "equinox:*";
- private static final String INPUT_SCANNER = "INPUT_SCANNER";
- private static final String SSH_INPUT_SCANNER = "SSH_INPUT_SCANNER";
- private static final String USER_STORAGE_PROPERTY_NAME = "osgi.console.ssh.useDefaultSecureStorage";
- private static final String DEFAULT_USER = "equinox";
private static final String TERMINAL_PROPERTY = "TERM";
- private static final String CLOSEABLE = "CLOSEABLE";
- private static final int ADD_USER_COUNTER_LIMIT = 2;
- public SshShell(CommandProcessor processor, BundleContext context) {
- this.processor = processor;
+ public SshShell(List<CommandProcessor> processors, BundleContext context) {
+ this.processors = processors;
this.context = context;
supportedEscapeSequences = new HashMap<String, TerminalTypeMappings> ();
supportedEscapeSequences.put("ANSI", new ANSITerminalTypeMappings());
@@ -102,7 +85,7 @@
this.callback = callback;
}
- public void start(Environment env) throws IOException {
+ public synchronized void start(Environment env) throws IOException {
String term = env.getEnv().get(TERMINAL_PROPERTY);
TerminalTypeMappings mapping = supportedEscapeSequences.get(term.toUpperCase());
if(mapping != null) {
@@ -110,73 +93,25 @@
currentEscapesToKey = mapping.getEscapesToKey();
}
- ConsoleInputStream input = new ConsoleInputStream();
- ConsoleOutputStream outp = new ConsoleOutputStream(out);
- SshInputHandler inputHandler = new SshInputHandler(in, input, outp);
- inputHandler.getScanner().setBackspace(currentMappings.getBackspace());
- inputHandler.getScanner().setDel(currentMappings.getDel());
- inputHandler.getScanner().setCurrentEscapesToKey(currentEscapesToKey);
- inputHandler.getScanner().setEscapes(currentMappings.getEscapes());
- inputHandler.start();
-
- ConsoleInputStream inp = new ConsoleInputStream();
- ConsoleInputHandler consoleInputHandler = new ConsoleInputHandler(input, inp, outp);
- consoleInputHandler.getScanner().setBackspace(currentMappings.getBackspace());
- consoleInputHandler.getScanner().setDel(currentMappings.getDel());
- consoleInputHandler.getScanner().setCurrentEscapesToKey(currentEscapesToKey);
- consoleInputHandler.getScanner().setEscapes(currentMappings.getEscapes());
- ((ConsoleInputScanner)consoleInputHandler.getScanner()).setContext(context);
- consoleInputHandler.start();
-
- final CommandSession session;
- final PrintStream output = new PrintStream(outp);
-
- session = processor.createSession(inp, output, output);
- session.put(SCOPE, EQUINOX_SCOPE);
- session.put(PROMPT, OSGI_PROMPT);
- session.put(INPUT_SCANNER, consoleInputHandler.getScanner());
- session.put(SSH_INPUT_SCANNER, inputHandler.getScanner());
- // Store this closeable object in the session, so that the disconnect command can close it
- session.put(CLOSEABLE, this);
- ((ConsoleInputScanner)consoleInputHandler.getScanner()).setSession(session);
-
- thread = new Thread() {
- public void run() {
- try {
- if ("true".equals(context.getProperty(USER_STORAGE_PROPERTY_NAME))) {
- String[] names = SecureUserStore.getUserNames();
- for (String name : names) {
- // if the default user is the only user, request creation of a new user and delete the default
- if (DEFAULT_USER.equals(name)) {
- if (names.length == 1) {
- session.getConsole().println("Currently the default user is the only one; since it will be deleted after first login, create a new user:");
- boolean isUserAdded =false;
- int count = 0;
- while (!isUserAdded && count < ADD_USER_COUNTER_LIMIT ){
- isUserAdded = ((Boolean) session.execute("addUser")).booleanValue();
- count++;
- }
- if (!isUserAdded) {
- break;
- }
- }
- if (SecureUserStore.existsUser(name)) {
- SecureUserStore.deleteUser(name);
- }
- break;
- }
- }
- }
- session.execute("gosh --login --noshutdown");
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- session.close();
- }
- }
- };
-
- thread.start();
+ for (CommandProcessor processor : processors) {
+ createNewSession(processor);
+ }
+ }
+
+ public synchronized void addCommandProcessor(CommandProcessor processor) {
+ createNewSession(processor);
+ }
+
+ public synchronized void removeCommandProcessor(CommandProcessor processor) {
+ Thread consoleSession = commandProcessorToConsoleThreadMap.get(processor);
+ if (consoleSession != null) {
+ consoleSession.interrupt();
+ }
+ }
+
+ private void createNewSession(CommandProcessor processor) {
+ SshSession consoleSession = startNewConsoleSession(processor);
+ commandProcessorToConsoleThreadMap.put(processor, consoleSession);
}
public void destroy() {
@@ -184,12 +119,36 @@
}
public void onExit() {
- thread.interrupt();
+ if (commandProcessorToConsoleThreadMap.values() != null) {
+ for (Thread consoleSession : commandProcessorToConsoleThreadMap.values()) {
+ consoleSession.interrupt();
+ }
+ }
callback.onExit(0);
}
-
- public void close() {
- onExit();
+
+ public void removeSession(SshSession session) {
+ CommandProcessor processorToRemove = null;
+ for (CommandProcessor processor : commandProcessorToConsoleThreadMap.keySet()) {
+ if (session.equals(commandProcessorToConsoleThreadMap.get(processor))) {
+ processorToRemove = processor;
+ break;
+ }
+ }
+
+ if (processorToRemove != null) {
+ commandProcessorToConsoleThreadMap.remove(processorToRemove);
+ }
+
+ if (commandProcessorToConsoleThreadMap.size() == 0) {
+ onExit();
+ }
+ }
+
+ private SshSession startNewConsoleSession(CommandProcessor processor) {
+ SshSession consoleSession = new SshSession(processor, context, this, in, out, currentMappings, currentEscapesToKey);
+ consoleSession.start();
+ return consoleSession;
}
}
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/SshShellFactory.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/SshShellFactory.java
index 9971193..82b7964 100644
--- a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/SshShellFactory.java
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/SshShellFactory.java
@@ -12,6 +12,7 @@
package org.eclipse.equinox.console.ssh;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import org.apache.felix.service.command.CommandProcessor;
@@ -25,21 +26,35 @@
*/
public class SshShellFactory implements Factory<Command> {
- private CommandProcessor processor;
+ private List<CommandProcessor> processors;
private BundleContext context;
private Set<SshShell> shells = new HashSet<SshShell>();
- public SshShellFactory(CommandProcessor processor, BundleContext context) {
- this.processor = processor;
+ public SshShellFactory(List<CommandProcessor> processors, BundleContext context) {
+ this.processors = processors;
this.context = context;
}
- public Command create() {
- SshShell shell = new SshShell(processor, context);
+ public synchronized Command create() {
+ SshShell shell = new SshShell(processors, context);
shells.add(shell);
return shell;
}
+ public synchronized void addCommandProcessor (CommandProcessor processor) {
+ processors.add(processor);
+ for (SshShell shell : shells) {
+ shell.addCommandProcessor(processor);
+ }
+ }
+
+ public synchronized void removeCommandProcessor (CommandProcessor processor) {
+ processors.remove(processor);
+ for (SshShell shell : shells) {
+ shell.removeCommandProcessor(processor);
+ }
+ }
+
public void exit() {
for(SshShell shell : shells) {
shell.onExit();
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/TelnetCommand.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/TelnetCommand.java
index 2d4bc58..5673a30 100644
--- a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/TelnetCommand.java
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/TelnetCommand.java
@@ -12,8 +12,10 @@
package org.eclipse.equinox.console.telnet;
import java.net.BindException;
+import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Hashtable;
+import java.util.List;
import org.apache.felix.service.command.CommandProcessor;
import org.apache.felix.service.command.Descriptor;
@@ -31,7 +33,7 @@
private String defaultHost = null;
private int defaultPort;
- private final CommandProcessor processor;
+ private List<CommandProcessor> processors = new ArrayList<CommandProcessor>();
private final BundleContext context;
private String host = null;
private int port;
@@ -49,7 +51,7 @@
public TelnetCommand(CommandProcessor processor, BundleContext context)
{
- this.processor = processor;
+ processors.add(processor);
this.context = context;
if ("true".equals(context.getProperty(USE_CONFIG_ADMIN_PROP))) {
Dictionary<String, String> telnetProperties = new Hashtable<String, String>();
@@ -154,7 +156,7 @@
}
try {
- telnetServer = new TelnetServer(context, processor, host, port);
+ telnetServer = new TelnetServer(context, processors, host, port);
} catch (BindException e) {
throw new Exception("Port " + port + " already in use");
}
@@ -171,6 +173,16 @@
}
}
+ public synchronized void addCommandProcessor(CommandProcessor processor) {
+ processors.add(processor);
+ telnetServer.addCommandProcessor(processor);
+ }
+
+ public synchronized void removeCommandProcessor(CommandProcessor processor) {
+ processors.remove(processor);
+ telnetServer.removeCommandProcessor(processor);
+ }
+
private void printHelp() {
StringBuffer help = new StringBuffer();
help.append("telnet - start simple telnet server");
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/TelnetServer.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/TelnetServer.java
index 019b1cf..3ea6189 100644
--- a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/TelnetServer.java
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/TelnetServer.java
@@ -16,27 +16,31 @@
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import org.apache.felix.service.command.CommandProcessor;
import org.osgi.framework.BundleContext;
/**
* A telnet server, which listens for telnet connections and starts a telnet connection manager
- * when a connection is accepted.
+ * when a connection is accepted. If there are multiple CommandProcessor, a telnet connection
+ * is created for each of them.
*
*/
public class TelnetServer extends Thread {
private ServerSocket server;
private boolean isRunning = true;
- private CommandProcessor processor;
+ private List<CommandProcessor> processors = null;
private BundleContext context;
- private List<TelnetConnection> telnetConnections = new ArrayList<TelnetConnection>();
+ private List<Socket> sockets = new ArrayList<Socket>();
+ private Map<CommandProcessor, List<TelnetConnection>> processorToConnectionsMapping = new HashMap<CommandProcessor, List<TelnetConnection>>();
- public TelnetServer(BundleContext context, CommandProcessor processor, String host, int port) throws IOException {
+ public TelnetServer(BundleContext context, List<CommandProcessor> processors, String host, int port) throws IOException {
this.context = context;
- this.processor = processor;
+ this.processors = processors;
if(host != null) {
server = new ServerSocket(port, 0, InetAddress.getByName(host));
} else {
@@ -51,9 +55,17 @@
while (isRunning)
{
final Socket socket = server.accept();
- TelnetConnection telnetConnection = new TelnetConnection(socket, processor, context);
- telnetConnections.add(telnetConnection);
- telnetConnection.start();
+ sockets.add(socket);
+ for (CommandProcessor processor : processors) {
+ TelnetConnection telnetConnection = new TelnetConnection(socket, processor, context);
+ List<TelnetConnection> telnetConnections = processorToConnectionsMapping.get(processor);
+ if (telnetConnections == null) {
+ telnetConnections = new ArrayList<TelnetConnection>();
+ processorToConnectionsMapping.put(processor, telnetConnections);
+ }
+ telnetConnections.add(telnetConnection);
+ telnetConnection.start();
+ }
}
} catch (IOException e) {
if (isRunning == true) {
@@ -71,6 +83,25 @@
}
}
+ public synchronized void addCommandProcessor(CommandProcessor processor) {
+ List<TelnetConnection> telnetConnections = new ArrayList<TelnetConnection>();
+ for (Socket socket : sockets) {
+ TelnetConnection telnetConnection = new TelnetConnection(socket, processor, context);
+ telnetConnections.add(telnetConnection);
+ telnetConnection.start();
+ }
+ processorToConnectionsMapping.put(processor, telnetConnections);
+ }
+
+ public synchronized void removeCommandProcessor(CommandProcessor processor) {
+ List<TelnetConnection> telnetConnections = processorToConnectionsMapping.remove(processor);
+ if (telnetConnections != null) {
+ for (TelnetConnection telnetConnection : telnetConnections) {
+ telnetConnection.close();
+ }
+ }
+ }
+
public synchronized void stopTelnetServer() {
isRunning = false;
try {
@@ -81,8 +112,10 @@
// do nothing
}
- for(TelnetConnection telnetConnection : telnetConnections) {
- telnetConnection.close();
+ for(List<TelnetConnection> telnetConnections : processorToConnectionsMapping.values()) {
+ for (TelnetConnection telnetConnection : telnetConnections) {
+ telnetConnection.close();
+ }
}
this.interrupt();