diff options
| author | Lazar Kirchev | 2011-10-14 12:40:24 +0000 |
|---|---|---|
| committer | Lazar Kirchev | 2011-10-14 12:40:24 +0000 |
| commit | 6a14fd0fdf67e8513b5ca5036e87e7affc13893d (patch) | |
| tree | b62b69b2009d58af33a5f2d6e54408043775ac13 | |
| parent | c9f670ec446cf4eb1c8663a9ecda41c5b130da43 (diff) | |
| download | rt.equinox.incubator-6a14fd0fdf67e8513b5ca5036e87e7affc13893d.tar.gz rt.equinox.incubator-6a14fd0fdf67e8513b5ca5036e87e7affc13893d.tar.xz rt.equinox.incubator-6a14fd0fdf67e8513b5ca5036e87e7affc13893d.zip | |
Refactor multiple CommandProcessors support.v20111017-1646
13 files changed, 347 insertions, 150 deletions
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 76212e1e..7fab9788 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 @@ public class SshCommandTests { 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 09a585b3..099442d7 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 @@ public class SshCommandWithConfigAdminTests { 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 index 91eb4544..8ab10b24 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.Before; 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 @@ public class SshDisconnectCommand { 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 @@ public class SshDisconnectCommand { 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 69a86429..9c041fdf 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.OutputStream; 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 @@ public class SshShellTests { 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 319912e4..21eb4c54 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.OutputStream; 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 @@ public class TelnetServerTests { 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 @@ public class TelnetServerTests { 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 56aac3ba..d6a4b4cb 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 @@ public class Activator implements BundleActivator { 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 @@ public class Activator implements BundleActivator { 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 @@ public class Activator implements BundleActivator { ServiceReference<CommandProcessor> reference, ServiceTracker<ConsoleSession, CommandSession> tracker) { tracker.close(); + CommandProcessor processor = context.getService(reference); + telnetConnection.removeCommandProcessor(processor); + sshConnection.removeCommandProcessor(processor); } } @@ -315,21 +327,18 @@ public class Activator implements BundleActivator { 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 2f4d1895..136509a8 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 @@ package org.eclipse.equinox.console.ssh; 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 @@ import org.osgi.service.cm.ManagedService; 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 @@ public class SshCommand { 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 @@ public class SshCommand { 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 class SshCommand { } } + 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 1f101790..a8cfed62 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 @@ public class SshServ extends Thread { 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 class SshServ extends Thread { } } + 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 00000000..38a67dba --- /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 99992a1b..f1f4839e 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.File; 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.TerminalTypeMappings; 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 @@ public class SshShell implements Command, Closeable { 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 @@ public class SshShell implements Command, Closeable { 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 class SshShell implements Command, Closeable { } 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 9971193d..82b7964d 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 @@ import org.osgi.framework.BundleContext; */ 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 2d4bc586..5673a303 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 @@ public class TelnetCommand { 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 class TelnetCommand { 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 @@ public class TelnetCommand { } 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 class TelnetCommand { } } + 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 019b1cf9..3ea61891 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.InetAddress; 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 @@ public class TelnetServer extends Thread { 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 class TelnetServer extends Thread { } } + 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 @@ public class TelnetServer extends Thread { // do nothing } - for(TelnetConnection telnetConnection : telnetConnections) { - telnetConnection.close(); + for(List<TelnetConnection> telnetConnections : processorToConnectionsMapping.values()) { + for (TelnetConnection telnetConnection : telnetConnections) { + telnetConnection.close(); + } } this.interrupt(); |
