Add command completion functionality and ssh support for the console. Migrate the Equinox FrameworkCommandProvider commands to RFC 147 commands.
diff --git a/console/org.eclipse.equinox.console.supportability/META-INF/MANIFEST.MF b/console/org.eclipse.equinox.console.supportability/META-INF/MANIFEST.MF
index 8ae9f7f..2b0c947 100644
--- a/console/org.eclipse.equinox.console.supportability/META-INF/MANIFEST.MF
+++ b/console/org.eclipse.equinox.console.supportability/META-INF/MANIFEST.MF
@@ -6,9 +6,23 @@
 Bundle-Activator: org.eclipse.equinox.console.command.adapter.Activator
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: J2SE-1.5
-Import-Package: org.apache.felix.service.command;status=provisional;version="0.6.1",
+Import-Package: javax.security.auth;resolution:=optional,
+ javax.security.auth.callback;resolution:=optional,
+ javax.security.auth.login;resolution:=optional,
+ javax.security.auth.spi;resolution:=optional,
+ org.apache.felix.service.command;status=provisional;version="0.8.0",
+ org.apache.sshd;version="0.5.0";resolution:=optional,
+ org.apache.sshd.common;version="0.5.0";resolution:=optional,
+ org.apache.sshd.server;version="0.5.0";resolution:=optional,
+ org.apache.sshd.server.jaas;version="0.5.0";resolution:=optional,
+ org.apache.sshd.server.keyprovider;version="0.5.0";resolution:=optional,
+ org.apache.sshd.server.session;version="0.5.0";resolution:=optional,
+ org.apache.sshd.server.shell;version="0.5.0";resolution:=optional,
  org.eclipse.osgi.framework.console,
+ org.eclipse.osgi.framework.internal.core,
  org.eclipse.osgi.internal.permadmin,
+ org.eclipse.osgi.internal.profile,
+ org.eclipse.osgi.service.environment,
  org.eclipse.osgi.service.resolver,
  org.eclipse.osgi.util,
  org.osgi.framework,
@@ -17,3 +31,13 @@
  org.osgi.service.permissionadmin,
  org.osgi.service.startlevel,
  org.osgi.util.tracker
+Export-Package: org.eclipse.equinox.console.command.adapter;x-friends:="org.eclipse.equinox.console.supportability.tests",
+ org.eclipse.equinox.console.commands;x-friends:="org.eclipse.equinox.console.supportability.tests",
+ org.eclipse.equinox.console.common;x-friends:="org.eclipse.equinox.console.supportability.tests",
+ org.eclipse.equinox.console.common.terminal;x-friends:="org.eclipse.equinox.console.supportability.tests",
+ org.eclipse.equinox.console.completion;x-friends:="org.eclipse.equinox.console.supportability.tests",
+ org.eclipse.equinox.console.jaas,
+ org.eclipse.equinox.console.ssh;x-friends:="org.eclipse.equinox.console.supportability.tests",
+ org.eclipse.equinox.console.storage;x-friends:="org.eclipse.equinox.console.supportability.tests",
+ org.eclipse.equinox.console.supportability;x-friends:="org.eclipse.equinox.console.supportability.tests",
+ org.eclipse.equinox.console.telnet;x-friends:="org.eclipse.equinox.console.supportability.tests"
diff --git a/console/org.eclipse.equinox.console.supportability/build.properties b/console/org.eclipse.equinox.console.supportability/build.properties
index 34d2e4d..aaefddf 100644
--- a/console/org.eclipse.equinox.console.supportability/build.properties
+++ b/console/org.eclipse.equinox.console.supportability/build.properties
@@ -1,4 +1,5 @@
 source.. = src/
 output.. = bin/
 bin.includes = META-INF/,\
-               .
+               .,\
+               bin/
diff --git a/console/org.eclipse.equinox.console.supportability/mina-core-2.0.2.jar b/console/org.eclipse.equinox.console.supportability/mina-core-2.0.2.jar
new file mode 100644
index 0000000..008cfa7
--- /dev/null
+++ b/console/org.eclipse.equinox.console.supportability/mina-core-2.0.2.jar
Binary files differ
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 ab38bf8..5e9f61c 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
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2010 IBM Corporation, SAP AG.
+ * Copyright (c) 2010, 2011 IBM Corporation, 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
@@ -23,6 +23,11 @@
 
 import org.apache.felix.service.command.CommandProcessor;
 import org.apache.felix.service.command.CommandSession;
+import org.apache.felix.service.command.Converter;
+import org.eclipse.equinox.console.commands.EquinoxCommandProvider;
+import org.eclipse.equinox.console.commands.EquinoxCommandsConverter;
+import org.eclipse.equinox.console.commands.HelpCommand;
+import org.eclipse.equinox.console.ssh.SshCommand;
 import org.eclipse.equinox.console.telnet.TelnetCommand;
 import org.eclipse.osgi.framework.console.CommandInterpreter;
 import org.eclipse.osgi.framework.console.CommandProvider;
@@ -31,6 +36,11 @@
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.condpermadmin.ConditionalPermissionAdmin;
+import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.service.startlevel.StartLevel;
+import org.osgi.service.permissionadmin.PermissionAdmin;
+import org.eclipse.osgi.service.resolver.PlatformAdmin;
 import org.osgi.util.tracker.ServiceTracker;
 import org.osgi.util.tracker.ServiceTrackerCustomizer;
 
@@ -38,12 +48,19 @@
  * The activator class controls the plug-in life cycle
  */
 public class Activator implements BundleActivator {
-
+	private ServiceTracker<StartLevel, ?> startLevelManagerTracker;
+	private ServiceTracker<ConditionalPermissionAdmin, ?> condPermAdminTracker;
+	private ServiceTracker<PermissionAdmin, ?> permissionAdminTracker;
+	private ServiceTracker<PackageAdmin, ?> packageAdminTracker;
+	private ServiceTracker<PlatformAdmin, ?> platformAdminTracker;
+	
 	private ServiceTracker<CommandProcessor, ServiceTracker<ConsoleSession, CommandSession>> commandProcessorTracker;
 	// Tracker for Equinox CommandProviders
 	private ServiceTracker<CommandProvider, List<ServiceRegistration<?>>> commandProviderTracker;
+	
+	private EquinoxCommandProvider equinoxCmdProvider;
 
-	public class ProcessorCustomizer implements
+	public static class ProcessorCustomizer implements
 			ServiceTrackerCustomizer<CommandProcessor, ServiceTracker<ConsoleSession, CommandSession>> {
 
 		private final BundleContext context;
@@ -57,20 +74,12 @@
 			CommandProcessor processor = context.getService(reference);
 			if (processor == null)
 				return null;
-			Dictionary<String, Object> properties = new Hashtable<String, Object>();
-			properties.put("osgi.command.scope", "equinox");
-			properties.put("osgi.command.function", "telnet");
+			
 			TelnetCommand telnet = new TelnetCommand(processor, context);
-			String consolePropValue = context.getProperty("osgi.console");
-			if(consolePropValue != null && !"".equals(consolePropValue)) {
-				try{
-					telnet.telnet(new String[]{"start"});
-				} catch (Exception e) {
-					System.out.println("Cannot start telnet. Reason: " + e.getMessage());
-					e.printStackTrace();
-				}
-			}
-			context.registerService(TelnetCommand.class.getName(), telnet, properties);
+			telnet.start();
+			SshCommand sshCommand = new SshCommand(processor, context);
+			sshCommand.start();
+			
 			ServiceTracker<ConsoleSession, CommandSession> tracker = new ServiceTracker<ConsoleSession, CommandSession>(context, ConsoleSession.class, new SessionCustomizer(context, processor));
 			tracker.open();
 			return tracker;
@@ -86,12 +95,11 @@
 			ServiceReference<CommandProcessor> reference,
 			ServiceTracker<ConsoleSession, CommandSession> tracker) {
 			tracker.close();
-		}
-
+		}	
 	}
 
-	// Privedes support for Equinox ConsoleSessions
-	public class SessionCustomizer implements
+	// Provides support for Equinox ConsoleSessions
+	public static class SessionCustomizer implements
 			ServiceTrackerCustomizer<ConsoleSession, CommandSession> {
 		private final BundleContext context;
 		final CommandProcessor processor;
@@ -111,6 +119,7 @@
 			new Thread(new Runnable(){
 				public void run() {
                     try {
+                    	gogoSession.put("prompt", "osgi> ");
                         gogoSession.execute("gosh --login --noshutdown");
                     }
                     catch (Exception e) {
@@ -187,6 +196,57 @@
 		commandProviderTracker.open();
 		commandProcessorTracker = new ServiceTracker<CommandProcessor, ServiceTracker<ConsoleSession,CommandSession>>(context, CommandProcessor.class, new ProcessorCustomizer(context));
 		commandProcessorTracker.open();
+		
+		condPermAdminTracker = new ServiceTracker<ConditionalPermissionAdmin, Object>(context, ConditionalPermissionAdmin.class.getName(), null);
+		condPermAdminTracker.open();
+
+		// grab permission admin
+		permissionAdminTracker = new ServiceTracker<PermissionAdmin, Object>(context, PermissionAdmin.class.getName(), null);
+		permissionAdminTracker.open();
+
+		startLevelManagerTracker = new ServiceTracker<StartLevel, Object>(context, StartLevel.class.getName(), null);
+		startLevelManagerTracker.open();
+
+		packageAdminTracker = new ServiceTracker<PackageAdmin, Object>(context, PackageAdmin.class.getName(), null);
+		packageAdminTracker.open();
+
+		platformAdminTracker = new ServiceTracker<PlatformAdmin, Object>(context, PlatformAdmin.class.getName(), null);
+		platformAdminTracker.open();
+		
+		equinoxCmdProvider = new EquinoxCommandProvider(context, this);
+		equinoxCmdProvider.start();
+		
+		HelpCommand helpCommand = new HelpCommand(context); 
+		helpCommand.start();
+	}
+	
+	public StartLevel getStartLevel() {
+		return (StartLevel) getServiceFromTracker(startLevelManagerTracker, StartLevel.class.getName());
+	}
+
+	public PermissionAdmin getPermissionAdmin() {
+		return (PermissionAdmin) getServiceFromTracker(permissionAdminTracker, PermissionAdmin.class.getName());
+	}
+
+	public ConditionalPermissionAdmin getConditionalPermissionAdmin() {
+		return (ConditionalPermissionAdmin) getServiceFromTracker(condPermAdminTracker, ConditionalPermissionAdmin.class.getName());
+	}
+
+	public PackageAdmin getPackageAdmin() {
+		return (PackageAdmin) getServiceFromTracker(packageAdminTracker, PackageAdmin.class.getName());
+	}
+
+	public PlatformAdmin getPlatformAdmin() {
+		return (PlatformAdmin) getServiceFromTracker(platformAdminTracker, PlatformAdmin.class.getName());
+	}
+	
+	private static Object getServiceFromTracker(ServiceTracker tracker, String serviceClass) {
+		if (tracker == null)
+			throw new IllegalStateException("Missing service: " + serviceClass);
+		Object result = tracker.getService();
+		if (result == null)
+			throw new IllegalStateException("Missing service: " + serviceClass);
+		return result;
 	}
 
 	Method[] getCommandMethods(Object command) {
@@ -212,8 +272,6 @@
 		String[] methodNames = new String[commandMethods.length];
 		for (int i = 0; i < commandMethods.length; i++) {
 			String methodName = "" + commandMethods[i].getName().substring(1);
-			if (methodName.equals("bundle"))
-				methodName = "x." + methodName;
 			methodNames[i] = methodName;
 		}
 
@@ -224,5 +282,8 @@
 	public void stop(BundleContext context) throws Exception {
 		commandProviderTracker.close();
 		commandProcessorTracker.close();
+		if (equinoxCmdProvider != null) {
+			equinoxCmdProvider.stop();
+		}
 	}
 }
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/command/adapter/CommandProviderAdapter.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/command/adapter/CommandProviderAdapter.java
index d2a46a2..08de8e8 100644
--- a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/command/adapter/CommandProviderAdapter.java
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/command/adapter/CommandProviderAdapter.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2010 IBM Corporation, SAP AG.
+ * Copyright (c) 2010, 2011 IBM Corporation, 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
@@ -39,8 +39,6 @@
 	public Object main(Object[] args) throws Exception {
 		try {
 			// first argument is the command
-			if ("x.bundle".equals(args[0]))
-				args[0] = "bundle"; // This is a hack to work around a current gogo bug
 			Method command = findCommand("_" + args[0]);
 			ArrayList<Object> argList = new ArrayList<Object>();
 			for (int i = 1; i < args.length; i++)
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/commands/ConsoleMessages.properties b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/commands/ConsoleMessages.properties
new file mode 100644
index 0000000..cb3eae0
--- /dev/null
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/commands/ConsoleMessages.properties
@@ -0,0 +1,79 @@
+###############################################################################
+# Copyright (c) 2003, 2011 IBM Corporation and others.
+# 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:
+#     IBM Corporation - initial API and implementation
+#     Lazar Kirchev, SAP AG - derivative implementation
+###############################################################################
+
+#External Messages for EN locale
+ 
+CONSOLE_ID=id
+CONSOLE_INVALID_INPUT=Invalid input.
+CONSOLE_NO_BUNDLE_SPECIFIED_ERROR=No bundle(s) specified!
+CONSOLE_NO_INSTALLED_BUNDLES_ERROR=No installed bundles.
+CONSOLE_BUNDLE_ID_MESSAGE=Bundle id is 
+CONSOLE_ID_MESSAGE=Id={0}
+CONSOLE_NO_EXPORTED_PACKAGES_MESSAGE=No exported packages
+CONSOLE_NO_IMPORTED_PACKAGES_MESSAGE=No imported packages
+CONSOLE_REMOVAL_PENDING_MESSAGE=removal pending
+CONSOLE_IMPORTS_MESSAGE=imports
+CONSOLE_STALE_MESSAGE=stale
+CONSOLE_NO_EXPORTED_PACKAGES_NO_PACKAGE_ADMIN_MESSAGE=No exported packages [PackageAdmin service is not registered]
+CONSOLE_NO_EXPORTED_PACKAGES_NO_PLATFORM_ADMIN_MESSAGE=No exported packages [PlatformAdmin service is not registered]
+CONSOLE_SERVICES_IN_USE_MESSAGE=Services in use:
+CONSOLE_NO_SERVICES_IN_USE_MESSAGE=No services in use.
+CONSOLE_STATUS_MESSAGE=Status={0}
+CONSOLE_DATA_ROOT_MESSAGE=Data Root={0}
+CONSOLE_EXPORTED_PACKAGES_MESSAGE=Exported packages
+CONSOLE_IMPORTED_PACKAGES_MESSAGE=Imported packages
+CONSOLE_EXPORTED_REMOVAL_PENDING_MESSAGE=[exported(removal pending)]
+CONSOLE_EXPORTED_MESSAGE=[exported]
+CONSOLE_TOTAL_MEMORY_MESSAGE=Total memory:
+CONSOLE_FREE_MEMORY_BEFORE_GARBAGE_COLLECTION_MESSAGE=Free memory before GC:
+CONSOLE_FREE_MEMORY_AFTER_GARBAGE_COLLECTION_MESSAGE=Free memory after GC:
+CONSOLE_MEMORY_GAINED_WITH_GARBAGE_COLLECTION_MESSAGE=Memory gained with GC:
+CONSOLE_FRAMEWORK_LAUNCHED_PLEASE_SHUTDOWN_MESSAGE=Framework is launched. Please shutdown framework first.
+CONSOLE_CAN_NOT_REFRESH_NO_PACKAGE_ADMIN_ERROR=Cannot refresh [PackageAdmin service is not registered]
+CONSOLE_CAN_NOT_USE_STARTLEVEL_NO_STARTLEVEL_SVC_ERROR=Cannot use Startlevel commands [Startlevel service is not registered]
+CONSOLE_NO_COMMAND_SPECIFIED_ERROR=No command specified
+CONSOLE_EXECUTED_RESULT_CODE_MESSAGE=Executed ({0}); result code = {1}
+CONSOLE_STARTED_IN_MESSAGE=Started({0}) in {1}
+CONSOLE_BUNDLE_HEADERS_TITLE=Bundle headers:
+CONSOLE_SYSTEM_PROPERTIES_TITLE=System properties:
+CONSOLE_NO_PARAMETERS_SPECIFIED_TITLE=No parameters specified:
+CONSOLE_SETTING_PROPERTIES_TITLE=Setting Properties:
+CONSOLE_STATE_BUNDLE_TITLE=State       Bundle
+CONSOLE_THREADGROUP_TITLE=ThreadGroupType:   Name:                 ParentGroup:   MaxP: Threads:
+CONSOLE_THREADTYPE_TITLE=ThreadType:        Name:                 ThreadGroup:   Prio:
+STARTLEVEL_FRAMEWORK_ACTIVE_STARTLEVEL=Framework Active Start Level = {0}
+STARTLEVEL_NO_STARTLEVEL_GIVEN=No Start Level given.
+STARTLEVEL_NO_STARTLEVEL_OR_BUNDLE_GIVEN=No Bundle or Start Level given.
+STARTLEVEL_INITIAL_BUNDLE_STARTLEVEL=Initial Bundle Start Level = {0}
+STARTLEVEL_BUNDLE_STARTLEVEL=Bundle {0} Start Level = {1}
+STARTLEVEL_POSITIVE_INTEGER=Startlevel must be a positive integer.
+CONSOLE_NAMED_CLASS_SPACE_MESSAGE=Named class space
+CONSOLE_PROVIDED_MESSAGE=[provided]
+CONSOLE_HOST_MESSAGE=Host bundles
+CONSOLE_NO_HOST_MESSAGE=No host bundles
+CONSOLE_FRAGMENT_MESSAGE=Fragment bundles
+CONSOLE_NO_FRAGMENT_MESSAGE=No fragment bundles
+CONSOLE_NO_NAMED_CLASS_SPACES_MESSAGE=No named class spaces
+CONSOLE_REQUIRED_BUNDLES_MESSAGE=Required bundles
+CONSOLE_NO_REQUIRED_BUNDLES_MESSAGE=No required bundles
+CONSOLE_REQUIRES_MESSAGE=[requires]
+CONSOLE_CANNOT_ACCESS_SYSTEM_PROPERTIES=Cannot set framework property
+CONSOLE_NOTHING_TO_INSTALL_ERROR=Nothing to install
+CONSOLE_REGISTERED_BY_BUNDLE_MESSAGE = "Registered by bundle:"
+CONSOLE_BUNDLES_USING_SERVICE_MESSAGE = "Bundles using service"
+CONSOLE_NO_BUNDLES_USING_SERVICE_MESSAGE = "No bundles using service."
+CONSOLE_NO_REGISTERED_SERVICES_MESSAGE = "No registered services."
+CONSOLE_FRAMEWORK_IS_LAUNCHED_MESSAGE = "Framework is launched."
+CONSOLE_FRAMEWORK_IS_SHUTDOWN_MESSAGE = "Framework is shutdown."
+CONSOLE_BUNDLE_LOCATION_MESSAGE = "Bundle Location"
+CONSOLE_STATE_BUNDLE_FILE_NAME_HEADER = "State Bundle File Name"
+CONSOLE_REGISTERED_SERVICES_MESSAGE = "Registered Services"
\ No newline at end of file
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/commands/ConsoleMsg.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/commands/ConsoleMsg.java
new file mode 100644
index 0000000..e58b82d
--- /dev/null
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/commands/ConsoleMsg.java
@@ -0,0 +1,148 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 20011 IBM Corporation and others.
+ * 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:
+ *     IBM Corporation - initial API and implementation
+ *     Lazar Kirchev, SAP AG - derivative implementation 
+ *******************************************************************************/
+package org.eclipse.equinox.console.commands;
+
+import org.eclipse.osgi.util.NLS;
+
+public class ConsoleMsg extends NLS {
+	public static final String BUNDLE_NAME = "org.eclipse.equinox.console.commands.ConsoleMessages"; //$NON-NLS-1$
+
+	public static String CONSOLE_INVALID_INPUT;
+	public static String CONSOLE_NO_BUNDLE_SPECIFIED_ERROR;
+	public static String CONSOLE_NOTHING_TO_INSTALL_ERROR;
+	public static String CONSOLE_BUNDLE_ID_MESSAGE;
+	public static String CONSOLE_NO_INSTALLED_BUNDLES_ERROR;
+	public static String CONSOLE_REGISTERED_SERVICES_MESSAGE;
+	public static String CONSOLE_FRAMEWORK_IS_LAUNCHED_MESSAGE;
+	public static String CONSOLE_FRAMEWORK_IS_SHUTDOWN_MESSAGE;
+	public static String CONSOLE_ID;
+	public static String CONSOLE_BUNDLE_LOCATION_MESSAGE;
+	public static String CONSOLE_STATE_BUNDLE_FILE_NAME_HEADER;
+	public static String CONSOLE_BUNDLES_USING_SERVICE_MESSAGE;
+	public static String CONSOLE_NO_REGISTERED_SERVICES_MESSAGE;
+	public static String CONSOLE_NO_BUNDLES_USING_SERVICE_MESSAGE;
+	public static String CONSOLE_REGISTERED_BY_BUNDLE_MESSAGE;
+	public static String CONSOLE_IMPORTS_MESSAGE;
+	public static String CONSOLE_STALE_MESSAGE;
+	public static String CONSOLE_NO_EXPORTED_PACKAGES_NO_PACKAGE_ADMIN_MESSAGE;
+	public static String CONSOLE_NO_EXPORTED_PACKAGES_NO_PLATFORM_ADMIN_MESSAGE;
+	public static String CONSOLE_NO_EXPORTED_PACKAGES_MESSAGE;
+	public static String CONSOLE_REMOVAL_PENDING_MESSAGE;
+	public static String CONSOLE_SERVICES_IN_USE_MESSAGE;
+	public static String CONSOLE_NO_SERVICES_IN_USE_MESSAGE;
+	public static String CONSOLE_ID_MESSAGE;
+	public static String CONSOLE_STATUS_MESSAGE;
+	public static String CONSOLE_DATA_ROOT_MESSAGE;
+
+	public static String CONSOLE_IMPORTED_PACKAGES_MESSAGE;
+	public static String CONSOLE_NO_IMPORTED_PACKAGES_MESSAGE;
+	public static String CONSOLE_HOST_MESSAGE;
+	public static String CONSOLE_EXPORTED_PACKAGES_MESSAGE;
+	public static String CONSOLE_EXPORTED_REMOVAL_PENDING_MESSAGE;
+	public static String CONSOLE_EXPORTED_MESSAGE;
+	public static String CONSOLE_NO_HOST_MESSAGE;
+	public static String CONSOLE_FRAGMENT_MESSAGE;
+	public static String CONSOLE_NO_FRAGMENT_MESSAGE;
+	public static String CONSOLE_NO_NAMED_CLASS_SPACES_MESSAGE;
+	public static String CONSOLE_NAMED_CLASS_SPACE_MESSAGE;
+	public static String CONSOLE_PROVIDED_MESSAGE;
+	public static String CONSOLE_REQUIRED_BUNDLES_MESSAGE;
+	public static String CONSOLE_NO_REQUIRED_BUNDLES_MESSAGE;
+	public static String CONSOLE_TOTAL_MEMORY_MESSAGE;
+	public static String CONSOLE_FREE_MEMORY_BEFORE_GARBAGE_COLLECTION_MESSAGE;
+	public static String CONSOLE_FREE_MEMORY_AFTER_GARBAGE_COLLECTION_MESSAGE;
+	public static String CONSOLE_MEMORY_GAINED_WITH_GARBAGE_COLLECTION_MESSAGE;
+	public static String CONSOLE_FRAMEWORK_LAUNCHED_PLEASE_SHUTDOWN_MESSAGE;
+	public static String CONSOLE_CAN_NOT_REFRESH_NO_PACKAGE_ADMIN_ERROR;
+	public static String CONSOLE_NO_COMMAND_SPECIFIED_ERROR;
+	public static String CONSOLE_STARTED_IN_MESSAGE;
+	public static String CONSOLE_EXECUTED_RESULT_CODE_MESSAGE;
+	public static String CONSOLE_BUNDLE_HEADERS_TITLE;
+	public static String CONSOLE_SYSTEM_PROPERTIES_TITLE;
+	public static String CONSOLE_NO_PARAMETERS_SPECIFIED_TITLE;
+	public static String CONSOLE_SETTING_PROPERTIES_TITLE;
+	public static String CONSOLE_STATE_BUNDLE_TITLE;
+	public static String CONSOLE_THREADGROUP_TITLE;
+	public static String CONSOLE_THREADTYPE_TITLE;
+	public static String CONSOLE_REQUIRES_MESSAGE;
+	public static String CONSOLE_CANNOT_ACCESS_SYSTEM_PROPERTIES;
+
+	public static String STARTLEVEL_FRAMEWORK_ACTIVE_STARTLEVEL;
+	public static String STARTLEVEL_BUNDLE_STARTLEVEL;
+	public static String STARTLEVEL_NO_STARTLEVEL_GIVEN;
+	public static String STARTLEVEL_NO_STARTLEVEL_OR_BUNDLE_GIVEN;
+	public static String STARTLEVEL_INITIAL_BUNDLE_STARTLEVEL;
+	public static String STARTLEVEL_POSITIVE_INTEGER;
+	
+	public static final String CONSOLE_HELP_EXIT_COMMAND_DESCRIPTION = "exit immediately (System.exit)";
+	public static final String CONSOLE_HELP_LAUNCH_COMMAND_DESCRIPTION = "start the OSGi Framework";
+	public static final String CONSOLE_HELP_SHUTDOWN_COMMAND_DESCRIPTION = "shutdown the OSGi Framework";
+	public static final String CONSOLE_HELP_START_COMMAND_DESCRIPTION = "start the specified bundle(s)";
+	public static final String CONSOLE_HELP_START_COMMAND_ARGUMENT_DESCRIPTION = "bundle(s) to start";
+	public static final String CONSOLE_HELP_STOP_COMMAND_DESCRIPTION = "stop the specified bundle(s)";
+	public static final String CONSOLE_HELP_STOP_COMMAND_ARGUMENT_DESCRIPTION = "bundle(s) to stop";
+	public static final String CONSOLE_HELP_INSTALL_COMMAND_DESCRIPTION = "install and optionally start bundle from the given URL";
+	public static final String CONSOLE_HELP_INSTALL_START_OPTION_DESCRIPTION = "spedify if the bundle should be started after installation";
+	public static final String CONSOLE_HELP_INSTALL_START_ARGUMENT_DESCRIPTION = "Location of bundle to install";
+	public static final String CONSOLE_HELP_UPDATE_COMMAND_DESCRIPTION = "update the specified bundle(s)";
+	public static final String CONSOLE_HELP_UPDATE_COMMAND_ARGUMENT_DESCRIPTION = "bundle(s) to update";
+	public static final String CONSOLE_HELP_UPDATE_SOURCE_COMMAND_DESCRIPTION = "Update the specified bundle from the specified location";
+	public static final String CONSOLE_HELP_UPDATE_SOURCE_COMMAND_BUNDLE_ARGUMENT_DESCRIPTION = "Bundle to update";
+	public static final String CONSOLE_HELP_UPDATE_SOURCE_COMMAND_URL_ARGUMENT_DESCRIPTION = "Location of the new bundle content";
+	public static final String CONSOLE_HELP_UNINSTALL_COMMAND_DESCRIPTION = "uninstall the specified bundle(s)";
+	public static final String CONSOLE_HELP_UNINSTALL_COMMAND_ARGUMENT_DESCRIPTION = "bundle(s) to uninstall";
+	public static final String CONSOLE_HELP_STATUS_COMMAND_DESCRIPTION = "display installed bundles and registered services";
+	public static final String CONSOLE_HELP_STATUS_ARGUMENT_DESCRIPTION = "[-s <comma separated list of bundle states>] [segment of bsn]";
+	public static final String CONSOLE_HELP_FILTER_ARGUMENT_DESCRIPTION = "Optional filter for filtering the displayed services. Examples for the filter: (objectClass=com.xyz.Person); (&(objectClass=com.xyz.Person)(sn=Jensen)); passing only com.xyz.Person is a shortcut for (objectClass=com.xyz.Person). The filter syntax specification is available at http://www.ietf.org/rfc/rfc1960.txt";
+	public static final String CONSOLE_HELP_SERVICES_COMMAND_DESCRIPTION = "display registered service details. Examples for [filter]: (objectClass=com.xyz.Person); (&(objectClass=com.xyz.Person)(sn=Jensen)); passing only com.xyz.Person is a shortcut for (objectClass=com.xyz.Person). The filter syntax specification is available at http://www.ietf.org/rfc/rfc1960.txt";
+	public static final String CONSOLE_HELP_PACKAGES_BUNDLE_ARGUMENT_DESCRIPTION = "Bundle whose packages to display. If not present displays all exported packages";
+	public static final String CONSOLE_HELP_PACKAGES_PACKAGE_ARGUMENT_DESCRIPTION = "Package name of the package to display";
+	public static final String CONSOLE_HELP_PACKAGES_COMMAND_DESCRIPTION = "display imported/exported package details";
+	public static final String CONSOLE_HELP_BUNDLES_COMMAND_DESCRIPTION = "display details for all installed bundles";
+	public static final String CONSOLE_HELP_IDLOCATION_ARGUMENT_DESCRIPTION = "(<id>|<location>)";
+	public static final String CONSOLE_HELP_BUNDLE_COMMAND_DESCRIPTION = "display details for the specified bundle(s)";
+	public static final String CONSOLE_HELP_GC_COMMAND_DESCRIPTION = "perform a garbage collection";
+	public static final String CONSOLE_HELP_INIT_COMMAND_DESCRIPTION = "uninstall all bundles";
+	public static final String CONSOLE_HELP_CLOSE_COMMAND_DESCRIPTION = "shutdown and exit";
+	public static final String CONSOLE_HELP_REFRESH_COMMAND_DESCRIPTION = "refresh the packages of the specified bundles";
+	public static final String CONSOLE_HELP_REFRESH_COMMAND_ARGUMENT_DESCRIPTION = "list of bundles whose packages to be refreshed; if not present refreshes all bundles";
+	public static final String CONSOLE_HELP_EXEC_COMMAND_DESCRIPTION = "execute a command in a separate process and wait";
+	public static final String CONSOLE_HELP_EXEC_COMMAND_ARGUMENT_DESCRIPTION = "command to be executed";
+	public static final String CONSOLE_HELP_FORK_COMMAND_DESCRIPTION = "execute a command in a separate process";
+	public static final String CONSOLE_HELP_FORK_COMMAND_ARGUMENT_DESCRIPTION = "command to be executed";
+	public static final String CONSOLE_HELP_HEADERS_COMMAND_DESCRIPTION = "print bundle headers";
+	public static final String CONSOLE_HELP_HEADERS_COMMAND_ARGUMENT_DESCRIPTION = "bundles to print headers for";
+	public static final String CONSOLE_PROPS_COMMAND_DESCRIPTION = "Display system properties";
+	public static final String CONSOLE_HELP_SETPROP_COMMAND_DESCRIPTION = "set OSGi properties";
+	public static final String CONSOLE_HELP_SETPROP_COMMAND_ARGUMENTS_DESCRIPTION = "list of properties with values to be set; the format is <key>=<value> and the pairs are separated with space if more than one";
+	public static final String CONSOLE_HELP_SS_COMMAND_DESCRIPTION = "display installed bundles (short status)";
+	public static final String CONSOLE_THREADS_COMMAND_DESCRIPTION = "display threads and thread groups";
+	public static final String CONSOLE_HELP_SL_COMMAND_DESCRIPTION = "display the start level for the specified bundle, or for the framework if no bundle specified";
+	public static final String CONSOLE_HELP_SL_COMMAND_ARGUMENT_DESCRIPTION = "bundle to get the start level";
+	public static final String CONSOLE_HELP_SETFWSL_COMMAND_DESCRIPTION = "set the framework start level";
+	public static final String CONSOLE_HELP_SETFWSL_COMMAND_ARGUMENT_DESCRIPTION = "new start level";
+	public static final String CONSOLE_HELP_SETBSL_COMMAND_DESCRIPTION = "set the start level for the bundle(s)";
+	public static final String CONSOLE_HELP_SETBSL_COMMAND_ARGUMENT_DESCRIPTION = "bundle(s) to change startlevel";
+	public static final String CONSOLE_HELP_SETIBSL_COMMAND_DESCRIPTION = "set the initial bundle start level";
+	public static final String CONSOLE_HELP_REQUIRED_BUNDLES_COMMAND_DESCRIPTION = "lists required bundles having the specified symbolic name";
+	public static final String CONSOLE_HELP_REQUIRED_BUNDLES_COMMAND_ARGUMENT_DESCRIPTION = "symbolic name for required bundles to be listed; if not specified all required bundles will be listed";
+	public static final String CONSOLE_HELP_PROFILELOG_COMMAND_DESCRIPTION = "Display & flush the profile log messages";
+	public static final String CONSOLE_HELP_VISIBLE_PACKAGES_COMMAND_DESCRIPTION = "lists all packages visible from the specified bundle";
+	public static final String CONSOLE_HELP_VISIBLE_PACKAGES_COMMAND_ARGUMENTS_DESCRIPTION = "bundle to list the visible packages";
+	public static final String CONSOLE_HELP_GETPROP_COMMAND_DESCRIPTION = "displays the system properties with the given name, or all of them";
+	public static final String CONSOLE_HELP_GETPROP_COMMAND_ARGUMENT_DESCRIPTION = "name of system property to dispaly";
+
+	static {
+		// initialize resource bundles
+		NLS.initializeMessages(BUNDLE_NAME, ConsoleMsg.class);
+	}
+}
\ No newline at end of file
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/commands/EquinoxCommandProvider.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/commands/EquinoxCommandProvider.java
new file mode 100644
index 0000000..3c80b87
--- /dev/null
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/commands/EquinoxCommandProvider.java
@@ -0,0 +1,1824 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2011 IBM Corporation and others.
+ * 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:
+ *     IBM Corporation - initial API and implementation
+ *     Lazar Kirchev, SAP AG - derivative implementation to migrate the commands from FrameworkCommandProvider to Gogo shell commands
+ *******************************************************************************/
+
+package org.eclipse.equinox.console.commands;
+
+import java.io.File;
+import java.lang.reflect.Field;
+import java.net.URL;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.Converter;
+import org.apache.felix.service.command.Descriptor;
+import org.apache.felix.service.command.Parameter;
+import org.eclipse.equinox.console.command.adapter.Activator;
+import org.eclipse.osgi.framework.internal.core.AbstractBundle;
+import org.eclipse.osgi.framework.internal.core.Util;
+import org.eclipse.osgi.internal.profile.Profile;
+import org.eclipse.osgi.service.environment.EnvironmentInfo;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.DisabledInfo;
+import org.eclipse.osgi.service.resolver.ExportPackageDescription;
+import org.eclipse.osgi.service.resolver.ImportPackageSpecification;
+import org.eclipse.osgi.service.resolver.PlatformAdmin;
+import org.eclipse.osgi.service.resolver.State;
+import org.eclipse.osgi.service.resolver.StateHelper;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.SynchronousBundleListener;
+import org.osgi.service.condpermadmin.ConditionalPermissionAdmin;
+import org.osgi.service.condpermadmin.ConditionalPermissionInfo;
+import org.osgi.service.condpermadmin.ConditionalPermissionUpdate;
+import org.osgi.service.packageadmin.ExportedPackage;
+import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.service.packageadmin.RequiredBundle;
+import org.osgi.service.permissionadmin.PermissionAdmin;
+import org.osgi.service.startlevel.StartLevel;
+
+/**
+ * This class provides methods to execute commands from the command line.  It registers
+ * itself as a service so it can be invoked by a CommandProcessor. 
+ *
+ * The commands provided by this class are:
+ ---Controlling the OSGi framework---
+ close - shutdown and exit
+ exit - exit immediately (System.exit)
+ gc - perform a garbage collection
+ init - uninstall all bundles
+ launch - start the Service Management Framework
+ setprop <key>=<value> - set the OSGI property
+ shutdown - shutdown the Service Management Framework
+ ---Controlliing Bundles---
+ install <url> {s[tart]} - install and optionally start bundle from the given URL
+ refresh (<id>|<location>) - refresh the packages of the specified bundles
+ start (<id>|<location>) - start the specified bundle(s)
+ stop (<id>|<location>) - stop the specified bundle(s)
+ uninstall (<id>|<location>) - uninstall the specified bundle(s)
+ update (<id>|<location>|<*>) - update the specified bundle(s)
+ ---Displaying Status---
+ bundle (<id>|<location>) - display details for the specified bundle(s)
+ bundles - display details for all installed bundles
+ headers (<id>|<location>) - print bundle headers
+ packages {<pkgname>|<id>|<location>} - display imported/exported package details
+ props - display System properties
+ services {filter} - display registered service details. Examples for [filter]: (objectClass=com.xyz.Person); (&(objectClass=com.xyz.Person)(|(sn=Jensen)(cn=Babs J*))); passing only com.xyz.Person is a shortcut for (objectClass=com.xyz.Person). The filter syntax specification is available at http://www.ietf.org/rfc/rfc1960.txt
+ ss - display installed bundles (short status)
+ status - display installed bundles and registered services
+ threads - display threads and thread groups
+ ---Extras---
+ exec <command> - execute a command in a separate process and wait
+ fork <command> - execute a command in a separate process
+ getprop <name> -  Displays the system properties with the given name, or all of them.
+ requiredBundles [<bsn>] - lists required bundles having the specified symbolic name or all if no bsn is specified
+ classSpaces [<bsn>] - lists required bundles having the specified symbolic name or all if no bsn is specified
+ ---Controlling StartLevel---
+ sl {(<id>|<location>)} - display the start level for the specified bundle, or for the framework if no bundle specified
+ setfwsl <start level> - set the framework start level
+ setbsl <start level> (<id>|<location>) - set the start level for the bundle(s)
+ setibsl <start level> - set the initial bundle start level
+ 
+*/
+
+public class EquinoxCommandProvider implements SynchronousBundleListener {
+
+	/** The system bundle context */
+	private final BundleContext context;
+	private ServiceRegistration<?> providerReg;
+	private ServiceRegistration<?> converterReg;
+
+	/** Strings used to format other strings */
+	private final static String tab = "\t"; //$NON-NLS-1$
+	private final static String newline = "\r\n"; //$NON-NLS-1$
+
+	/** this list contains the bundles known to be lazily awaiting activation */
+	private final List<Bundle> lazyActivation = new ArrayList<Bundle>();
+	
+	private Activator activator;
+	
+	/** commands provided by this command provider */ 
+	private static final String[] functions = new String[] {"exit", "shutdown", "sta", "start", "sto", "stop", "i", 
+		"install", "up", "up", "up", "update", "update", "update", "un", "uninstall", "s", "status", "se", "services",
+		"p", "p", "packages", "packages", "bundles", "b", "bundle", "gc", "init", "close", "r", "refresh", "exec",
+		"fork", "h", "headers", "pr", "props", "setp", "setprop", "ss", "t", "threads", "sl", "setfwsl", "setbsl",
+		"setibsl", "requiredBundles", "classSpaces", "profilelog", "getPackages", "getprop"};
+	
+	/**
+	 *  Constructor.
+	 *
+	 *  start() must be called after creating this object.
+	 *
+	 *  @param framework The current instance of the framework
+	 */
+	public EquinoxCommandProvider(BundleContext context, Activator activator) {
+		this.context = context;
+		this.activator = activator;
+	}
+
+	/**
+	 *  Starts this CommandProvider.
+	 *
+	 *  Registers this object as a service providing commands
+	 *  Adds this object as a SynchronousBundleListener.
+	 */
+	public void start() {
+		EquinoxCommandsConverter converter = new EquinoxCommandsConverter(context);
+		converterReg = context.registerService(Converter.class.getName(), converter, null);
+		
+		Dictionary<String, Object> props = new Hashtable<String, Object>();
+		props.put(Constants.SERVICE_RANKING, new Integer(Integer.MAX_VALUE));
+		props.put(CommandProcessor.COMMAND_SCOPE, "equinox");
+		props.put(CommandProcessor.COMMAND_FUNCTION, functions);
+		providerReg = context.registerService(EquinoxCommandProvider.class.getName(), this, props);
+		context.addBundleListener(this);
+	}
+
+	public void stop() {
+		if (converterReg != null) {
+			converterReg.unregister();
+		}
+		
+		context.removeBundleListener(this);
+		
+		if (providerReg != null) {
+			providerReg.unregister();
+		}
+	}
+
+	/**
+	 *  Handle the exit command.  Exit immediately (System.exit)
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_EXIT_COMMAND_DESCRIPTION)
+	public void exit() throws Exception {
+		System.out.println();
+		System.exit(0);
+	}
+
+	/**
+	 *  Handle the shutdown command.  Shutdown the OSGi framework.
+	 *
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_SHUTDOWN_COMMAND_DESCRIPTION)
+	public void shutdown() throws Exception {
+		context.getBundle(0).stop();
+	}
+
+	/**
+	 *  Handle the start command's abbreviation.  Invoke start()
+	 *
+	 *  @param bundles bundle(s) to be started
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_START_COMMAND_DESCRIPTION)
+	public void sta(@Descriptor(ConsoleMsg.CONSOLE_HELP_START_COMMAND_ARGUMENT_DESCRIPTION) Bundle[] bundles) throws Exception {
+		start(bundles);
+	}
+
+	/**
+	 *  Handle the start command.  Start the specified bundle(s).
+	 *
+	 *  @param bundles bundle(s) to be started
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_START_COMMAND_DESCRIPTION)
+	public void start(@Descriptor(ConsoleMsg.CONSOLE_HELP_START_COMMAND_ARGUMENT_DESCRIPTION) Bundle[] bundles) throws Exception {
+		if (bundles == null) {
+			System.out.println(ConsoleMsg.CONSOLE_NO_BUNDLE_SPECIFIED_ERROR);
+			return;
+		}
+		
+		if (bundles.length == 0) {
+			System.out.println(ConsoleMsg.CONSOLE_NO_BUNDLE_SPECIFIED_ERROR);
+			return;
+		}
+		
+		for(Bundle bundle : bundles) {
+				bundle.start();
+		}
+	}
+
+	/**
+	 *  Handle the stop command's abbreviation.  Invoke stop()
+	 *
+	 *  @param bundles bundle(s) to be stopped.
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_STOP_COMMAND_DESCRIPTION)
+	public void sto(@Descriptor(ConsoleMsg.CONSOLE_HELP_STOP_COMMAND_ARGUMENT_DESCRIPTION) Bundle[] bundles) throws Exception {
+		stop(bundles);
+	}
+
+	/**
+	 *  Handle the stop command.  Stop the specified bundle(s).
+	 *
+	 *  @param bundles bundle(s) to be stopped.
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_STOP_COMMAND_DESCRIPTION)
+	public void stop(@Descriptor(ConsoleMsg.CONSOLE_HELP_STOP_COMMAND_ARGUMENT_DESCRIPTION) Bundle[] bundles) throws Exception {
+		if (bundles == null) {
+			System.out.println(ConsoleMsg.CONSOLE_NO_BUNDLE_SPECIFIED_ERROR);
+			return;
+		}
+		
+		if (bundles.length == 0) {
+			System.out.println(ConsoleMsg.CONSOLE_NO_BUNDLE_SPECIFIED_ERROR);
+			return;
+		}
+		
+		for(Bundle bundle : bundles) {
+				bundle.stop();
+		}
+	}
+
+	/**
+	 *  Handle the install command's abbreviation.  Invoke install()
+	 *
+	 *	@param shouldStart if the bundle should be start after installation
+	 *  @param url location of the bundle to be installed
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_INSTALL_COMMAND_DESCRIPTION)
+	public void i(
+			@Descriptor(ConsoleMsg.CONSOLE_HELP_INSTALL_START_OPTION_DESCRIPTION)
+			@Parameter(absentValue = "false", presentValue = "true", names = { "-start" })
+			boolean shouldStart,
+			@Descriptor(ConsoleMsg.CONSOLE_HELP_INSTALL_START_ARGUMENT_DESCRIPTION) String url) throws Exception {
+		install(shouldStart, url);
+	}
+
+	/**
+	 *  Handle the install command.  Install and optionally start bundle from the given URL
+	 *
+	 *  @param shouldStart if the bundle should be start after installation
+	 *  @param url location of the bundle to be installed
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_INSTALL_COMMAND_DESCRIPTION)
+	public Bundle install(
+			@Descriptor(ConsoleMsg.CONSOLE_HELP_INSTALL_START_OPTION_DESCRIPTION)
+			@Parameter(absentValue = "false", presentValue = "true", names = { "-start" })
+			boolean shouldStart,
+			@Descriptor(ConsoleMsg.CONSOLE_HELP_INSTALL_START_ARGUMENT_DESCRIPTION) String url) throws Exception {
+		if (url == null) {
+			System.out.println(ConsoleMsg.CONSOLE_NOTHING_TO_INSTALL_ERROR);
+			return null;
+		} else {
+			Bundle bundle = context.installBundle(url);
+			System.out.print(ConsoleMsg.CONSOLE_BUNDLE_ID_MESSAGE);
+			System.out.println(bundle.getBundleId());
+			if (shouldStart == true) {
+				bundle.start();
+			}
+			return bundle;
+		}
+	}
+
+	private static boolean matchCommand(String command, String input, int minLength) {
+		if (minLength <= 0)
+			minLength = command.length();
+		int length = input.length();
+		if (minLength > length)
+			length = minLength;
+		return (command.regionMatches(0, input, 0, length));
+	}
+
+	/**
+	 *  Handle the update command's abbreviation.  Invoke update()
+	 *
+	 *  @param bundles bundle(s) to be updated
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_UPDATE_COMMAND_DESCRIPTION)
+	public void up(@Descriptor(ConsoleMsg.CONSOLE_HELP_UPDATE_COMMAND_ARGUMENT_DESCRIPTION) Bundle[] bundles) throws Exception {
+		update(bundles);
+	}
+	
+	/**
+	 *  Handle the update command's abbreviation.  Invoke update()
+	 *
+	 *  @param bundle bundle to be updated
+	 *  @param source location to get the new bundle's content
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_UPDATE_SOURCE_COMMAND_DESCRIPTION)
+	public void up(
+			@Descriptor(ConsoleMsg.CONSOLE_HELP_UPDATE_SOURCE_COMMAND_BUNDLE_ARGUMENT_DESCRIPTION)
+			Bundle bundle, 
+			@Descriptor(ConsoleMsg.CONSOLE_HELP_UPDATE_SOURCE_COMMAND_URL_ARGUMENT_DESCRIPTION)
+			URL source) throws Exception {
+		update(bundle, source);
+	}
+	
+	/**
+	 *  Handle the update command.  Update the specified bundle(s).
+	 *
+	 *  @param bundles bundle(s) to be updated
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_UPDATE_COMMAND_DESCRIPTION)
+	public void update(@Descriptor(ConsoleMsg.CONSOLE_HELP_UPDATE_COMMAND_ARGUMENT_DESCRIPTION) Bundle[] bundles) throws Exception {
+		if (bundles == null) {
+			System.out.println(ConsoleMsg.CONSOLE_NO_BUNDLE_SPECIFIED_ERROR);
+			return;
+		}
+		
+		if(bundles.length == 0) {
+			System.out.println(ConsoleMsg.CONSOLE_NO_BUNDLE_SPECIFIED_ERROR);
+			return;
+		}
+		
+		for(Bundle bundle : bundles) {
+			bundle.update();
+		}
+	}
+
+	/**
+	 *  Handle the update command.  Update the specified bundle with the specified content.
+	 *
+	 *  @param bundle bundle to be updated
+	 *  @param source location to get the new bundle's content
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_UPDATE_SOURCE_COMMAND_DESCRIPTION)
+	public void update(
+			@Descriptor(ConsoleMsg.CONSOLE_HELP_UPDATE_SOURCE_COMMAND_BUNDLE_ARGUMENT_DESCRIPTION)
+			Bundle bundle, 
+			@Descriptor(ConsoleMsg.CONSOLE_HELP_UPDATE_SOURCE_COMMAND_URL_ARGUMENT_DESCRIPTION)
+			URL source) throws Exception {
+		bundle.update(source.openStream());
+	}
+
+	/**
+	 *  Handle the uninstall command's abbreviation.  Invoke uninstall()
+	 *
+	 *  @param bundles bundle(s) to uninstall
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_UNINSTALL_COMMAND_DESCRIPTION)
+	public void un(@Descriptor(ConsoleMsg.CONSOLE_HELP_UNINSTALL_COMMAND_ARGUMENT_DESCRIPTION) Bundle[] bundles) throws Exception {
+		uninstall(bundles);
+	}
+
+	/**
+	 *  Handle the uninstall command.  Uninstall the specified bundle(s).
+	 *
+	 *  @param bundles bundle(s) to uninstall
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_UNINSTALL_COMMAND_DESCRIPTION)
+	public void uninstall(@Descriptor(ConsoleMsg.CONSOLE_HELP_UNINSTALL_COMMAND_ARGUMENT_DESCRIPTION) Bundle[] bundles) throws Exception {
+		if(bundles == null) {
+			System.out.println(ConsoleMsg.CONSOLE_NO_BUNDLE_SPECIFIED_ERROR);
+			return;
+		}
+		
+		if(bundles.length == 0) {
+			System.out.println(ConsoleMsg.CONSOLE_NO_BUNDLE_SPECIFIED_ERROR);
+			return;
+		}
+		
+		for (Bundle bundle : bundles) {
+				bundle.uninstall();
+		}
+	}
+
+	private int getStatesFromConstants(String states) throws IllegalArgumentException{
+		int stateFilter = -1;
+		if(!states.equals("")) {
+			StringTokenizer tokens = new StringTokenizer(states, ","); //$NON-NLS-1
+			while (tokens.hasMoreElements()) {
+				String desiredState = (String) tokens.nextElement();
+				Field match = null;
+				try {
+					match = Bundle.class.getField(desiredState.toUpperCase());
+					if (stateFilter == -1)
+						stateFilter = 0;
+					stateFilter |= match.getInt(match);
+				} catch (NoSuchFieldException e) {
+					System.out.println(ConsoleMsg.CONSOLE_INVALID_INPUT + ": " + desiredState); //$NON-NLS-1$
+					throw new IllegalArgumentException();
+				} catch (IllegalAccessException e) {
+					System.out.println(ConsoleMsg.CONSOLE_INVALID_INPUT + ": " + desiredState); //$NON-NLS-1$
+					throw new IllegalArgumentException();
+				}
+			}
+		}
+		return stateFilter;
+	}
+	
+	/**
+	 *  Handle the status command's abbreviation.  Invoke status()
+	 *
+	 *  @param arguments 
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_STATUS_COMMAND_DESCRIPTION)
+	public void s(@Descriptor(ConsoleMsg.CONSOLE_HELP_STATUS_ARGUMENT_DESCRIPTION) String... arguments) throws Exception {
+		status(arguments);
+	}
+
+	/**
+	 *  Handle the status command.  Display installed bundles and registered services.
+	 *
+	 *  @param arguments
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_STATUS_COMMAND_DESCRIPTION)
+	public void status(@Descriptor(ConsoleMsg.CONSOLE_HELP_STATUS_ARGUMENT_DESCRIPTION) String... arguments) throws Exception {
+		if (context.getBundle(0).getState() == Bundle.ACTIVE) {
+			System.out.println(ConsoleMsg.CONSOLE_FRAMEWORK_IS_LAUNCHED_MESSAGE);
+		} else {
+			System.out.println(ConsoleMsg.CONSOLE_FRAMEWORK_IS_SHUTDOWN_MESSAGE);
+		}
+		System.out.println();
+		
+		String states = "";
+		String[] bsnSegments = null;
+		
+		if(arguments != null && arguments.length > 0) {
+			if(arguments[0].equals("-s")) {
+				if (arguments.length > 1) {
+					states = arguments[1];
+					if(arguments.length > 2) {
+						bsnSegments = new String[arguments.length - 2];
+						System.arraycopy(arguments, 2, bsnSegments, 0, bsnSegments.length);
+					}
+				}
+			} else {
+				bsnSegments = arguments;
+			}
+		}
+
+		int stateFilter;
+		
+		try {
+			stateFilter = getStatesFromConstants(states);
+		} catch (IllegalArgumentException e) {
+			return;
+		}
+		
+		Bundle[] bundles = context.getBundles();
+		int size = bundles.length;
+
+		if (size == 0) {
+			System.out.println(ConsoleMsg.CONSOLE_NO_INSTALLED_BUNDLES_ERROR);
+			return;
+		}
+		
+		System.out.print(ConsoleMsg.CONSOLE_ID);
+		System.out.print(tab);
+		System.out.println(ConsoleMsg.CONSOLE_BUNDLE_LOCATION_MESSAGE);
+		System.out.println(ConsoleMsg.CONSOLE_STATE_BUNDLE_FILE_NAME_HEADER);
+		for (int i = 0; i < size; i++) {
+			Bundle bundle = bundles[i];
+			if (!match(bundle, bsnSegments, stateFilter))
+				continue;
+			System.out.print(bundle.getBundleId());
+			System.out.print(tab);
+			System.out.println(bundle.getLocation());
+			System.out.print("  "); //$NON-NLS-1$
+			System.out.print(getStateName(bundle));
+			System.out.println(bundle.toString());
+		}
+
+		ServiceReference<?>[] services = context.getServiceReferences((String) null, (String) null);
+		if (services != null) {
+			System.out.println(ConsoleMsg.CONSOLE_REGISTERED_SERVICES_MESSAGE);
+			for(ServiceReference<?> service : services) {
+				System.out.println(service);
+			}
+		}
+	}
+
+	/**
+	 *  Handle the services command's abbreviation.  Invoke services()
+	 *
+	 *  @param filters filters for services
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_SERVICES_COMMAND_DESCRIPTION)
+	public void se(@Descriptor(ConsoleMsg.CONSOLE_HELP_FILTER_ARGUMENT_DESCRIPTION)String... filters) throws Exception {
+		services(filters);
+	}
+
+	/**
+	 *  Handle the services command.  Display registered service details.
+	 *
+	 *  @param filters filters for services
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_SERVICES_COMMAND_DESCRIPTION)
+	public void services(@Descriptor(ConsoleMsg.CONSOLE_HELP_FILTER_ARGUMENT_DESCRIPTION)String... filters) throws Exception {
+		String filter = null;
+		if (filters != null && filters.length > 0) {
+			StringBuffer buf = new StringBuffer();
+			for (String singleFilter : filters) {
+				buf.append(' ');
+				buf.append(singleFilter);
+			}
+			filter = buf.toString();
+		}
+
+		InvalidSyntaxException originalException = null;
+		ServiceReference<?>[] services = null;
+
+		try {
+			services = context.getServiceReferences((String) null, filter);
+		} catch (InvalidSyntaxException e) {
+			originalException = e;
+		}
+
+		if (filter != null) {
+			filter = filter.trim();
+		}
+		// If the filter is invalid and does not start with a bracket, probably the argument was the name of an interface.
+		// Try to construct an object class filter with this argument, and if still invalid - throw the original InvalidSyntaxException
+		if (originalException != null && !filter.startsWith("(") && !filter.contains(" ")) { //$NON-NLS-1$ //$NON-NLS-2$
+			try {
+				filter = "(" + Constants.OBJECTCLASS + "=" + filter + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+				services = context.getServiceReferences((String) null, filter);
+			} catch (InvalidSyntaxException e) {
+				throw originalException;
+			}
+		} else if (originalException != null) {
+			throw originalException;
+		}
+
+		if (services != null) {
+			int size = services.length;
+			if (size > 0) {
+				for (int j = 0; j < size; j++) {
+					ServiceReference<?> service = services[j];
+					System.out.println(service);
+					System.out.print("  "); //$NON-NLS-1$
+					System.out.print(ConsoleMsg.CONSOLE_REGISTERED_BY_BUNDLE_MESSAGE);
+					System.out.print(" "); //$NON-NLS-1$
+					System.out.println(service.getBundle());
+					Bundle[] users = service.getUsingBundles();
+					if (users != null) {
+						System.out.print("  "); //$NON-NLS-1$
+						System.out.println(ConsoleMsg.CONSOLE_BUNDLES_USING_SERVICE_MESSAGE);
+						for (int k = 0; k < users.length; k++) {
+							System.out.print("    "); //$NON-NLS-1$
+							System.out.println(users[k]);
+						}
+					} else {
+						System.out.print("  "); //$NON-NLS-1$
+						System.out.println(ConsoleMsg.CONSOLE_NO_BUNDLES_USING_SERVICE_MESSAGE);
+					}
+				}
+				return;
+			}
+		}
+		System.out.println(ConsoleMsg.CONSOLE_NO_REGISTERED_SERVICES_MESSAGE);
+	}
+
+	/**
+	 *  Handle the packages command's abbreviation.  Invoke packages()
+	 *
+	 *  @param bundle bundle for which to display package details
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_PACKAGES_COMMAND_DESCRIPTION)
+	public void p(@Descriptor(ConsoleMsg.CONSOLE_HELP_PACKAGES_BUNDLE_ARGUMENT_DESCRIPTION)Bundle... bundle) throws Exception {
+		packages(bundle);
+	}
+	
+	/**
+	 *  Handle the packages command's abbreviation.  Invoke packages()
+	 *
+	 *  @param packageName package for which to display details
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_PACKAGES_COMMAND_DESCRIPTION)
+	public void p(@Descriptor(ConsoleMsg.CONSOLE_HELP_PACKAGES_PACKAGE_ARGUMENT_DESCRIPTION)String packageName) throws Exception {
+		packages(packageName);
+	}
+	
+	/**
+	 *  Handle the packages command.  Display imported/exported packages details.
+	 *
+	 *  @param bundle bundle for which to display package details
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_PACKAGES_COMMAND_DESCRIPTION)
+	public void packages(@Descriptor(ConsoleMsg.CONSOLE_HELP_PACKAGES_BUNDLE_ARGUMENT_DESCRIPTION)Bundle... bundle) throws Exception {
+		if(activator.getPackageAdmin() != null) {
+			ExportedPackage[] exportedPackages;
+			if(bundle != null && bundle.length > 0) {
+				exportedPackages = activator.getPackageAdmin().getExportedPackages(bundle[0]);
+			} else {
+				exportedPackages = activator.getPackageAdmin().getExportedPackages((Bundle) null);
+			}
+			getPackages(exportedPackages);
+		} else {
+			System.out.println(ConsoleMsg.CONSOLE_NO_EXPORTED_PACKAGES_NO_PACKAGE_ADMIN_MESSAGE);
+		}
+	}
+	
+	/**
+	 *  Handle the packages command.  Display imported/exported packages details.
+	 *
+	 *  @param packageName package for which to display details
+	 **/
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_PACKAGES_COMMAND_DESCRIPTION)
+	public void packages(@Descriptor(ConsoleMsg.CONSOLE_HELP_PACKAGES_PACKAGE_ARGUMENT_DESCRIPTION)String packageName) throws Exception {
+		if(activator.getPackageAdmin() != null) {
+			ExportedPackage[] exportedPackages = activator.getPackageAdmin().getExportedPackages(packageName);
+			getPackages(exportedPackages);
+		} else {
+			System.out.println(ConsoleMsg.CONSOLE_NO_EXPORTED_PACKAGES_NO_PACKAGE_ADMIN_MESSAGE);
+		}
+	}
+	
+	private void getPackages(ExportedPackage[] packages) throws Exception {
+		if (packages == null) {
+			System.out.println(ConsoleMsg.CONSOLE_NO_EXPORTED_PACKAGES_MESSAGE);
+			return;
+		}
+		for (int i = 0; i < packages.length; i++) {
+			org.osgi.service.packageadmin.ExportedPackage pkg = packages[i];
+			System.out.print(pkg);
+
+			boolean removalPending = pkg.isRemovalPending();
+			if (removalPending) {
+				System.out.print("("); //$NON-NLS-1$
+				System.out.print(ConsoleMsg.CONSOLE_REMOVAL_PENDING_MESSAGE);
+				System.out.println(")"); //$NON-NLS-1$
+			}
+
+			org.osgi.framework.Bundle exporter = pkg.getExportingBundle();
+			if (exporter != null) {
+				System.out.print("<"); //$NON-NLS-1$
+				System.out.print(exporter);
+				System.out.println(">"); //$NON-NLS-1$
+
+				org.osgi.framework.Bundle[] importers = pkg.getImportingBundles();
+				for (int j = 0; j < importers.length; j++) {
+					System.out.print("  "); //$NON-NLS-1$
+					System.out.print(importers[j]);
+					System.out.print(" "); //$NON-NLS-1$
+					System.out.println(ConsoleMsg.CONSOLE_IMPORTS_MESSAGE);
+				}
+			} else {
+				System.out.print("<"); //$NON-NLS-1$
+				System.out.print(ConsoleMsg.CONSOLE_STALE_MESSAGE);
+				System.out.println(">"); //$NON-NLS-1$
+			}
+		}
+	}
+
+	/**
+	 *  Handle the bundles command.  Display details for all installed bundles.
+	 *
+	 *  @param arguments
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_BUNDLES_COMMAND_DESCRIPTION)
+	public void bundles(@Descriptor(ConsoleMsg.CONSOLE_HELP_STATUS_ARGUMENT_DESCRIPTION) String... arguments) throws Exception {
+		String states = "";
+		String[] bsnSegments = null;
+		
+		if(arguments != null && arguments.length > 0) {
+			if(arguments[0].equals("-s")) {
+				if (arguments.length > 1) {
+					states = arguments[1];
+					if(arguments.length > 2) {
+						bsnSegments = new String[arguments.length - 2];
+						System.arraycopy(arguments, 2, bsnSegments, 0, bsnSegments.length);
+					}
+				}
+			} else {
+				bsnSegments = arguments;
+			}
+		}
+		int stateFilter;
+		try {
+			stateFilter = getStatesFromConstants(states);
+		} catch (IllegalArgumentException e) {
+			return;
+		}
+
+		Bundle[] bundles = context.getBundles();
+		int size = bundles.length;
+
+		if (size == 0) {
+			System.out.println(ConsoleMsg.CONSOLE_NO_INSTALLED_BUNDLES_ERROR);
+			return;
+		}
+
+		for (int i = 0; i < size; i++) {
+			Bundle bundle = bundles[i];
+			if (!match(bundle, bsnSegments, stateFilter))
+				continue;
+			long id = bundle.getBundleId();
+			System.out.println(bundle);
+			System.out.print("  "); //$NON-NLS-1$
+			System.out.print(NLS.bind(ConsoleMsg.CONSOLE_ID_MESSAGE, String.valueOf(id)));
+			System.out.print(", "); //$NON-NLS-1$
+			System.out.print(NLS.bind(ConsoleMsg.CONSOLE_STATUS_MESSAGE, getStateName(bundle)));
+			if (id != 0) {
+				File dataRoot = bundle.getDataFile(""); //$NON-NLS-1$
+				String root = (dataRoot == null) ? null : dataRoot.getAbsolutePath();
+				System.out.print(NLS.bind(ConsoleMsg.CONSOLE_DATA_ROOT_MESSAGE, root));
+			} else {
+				System.out.println();
+			}
+
+			ServiceReference<?>[] services = bundle.getRegisteredServices();
+			if (services != null) {
+				System.out.print("  "); //$NON-NLS-1$
+				System.out.println(ConsoleMsg.CONSOLE_REGISTERED_SERVICES_MESSAGE);
+				for (int j = 0; j < services.length; j++) {
+					System.out.print("    "); //$NON-NLS-1$
+					System.out.println(services[j]);
+				}
+			} else {
+				System.out.print("  "); //$NON-NLS-1$
+				System.out.println(ConsoleMsg.CONSOLE_NO_REGISTERED_SERVICES_MESSAGE);
+			}
+
+			services = bundle.getServicesInUse();
+			if (services != null) {
+				System.out.print("  "); //$NON-NLS-1$
+				System.out.println(ConsoleMsg.CONSOLE_SERVICES_IN_USE_MESSAGE);
+				for (int j = 0; j < services.length; j++) {
+					System.out.print("    "); //$NON-NLS-1$
+					System.out.println(services[j]);
+				}
+			} else {
+				System.out.print("  "); //$NON-NLS-1$
+				System.out.println(ConsoleMsg.CONSOLE_NO_SERVICES_IN_USE_MESSAGE);
+			}
+		}
+	}
+
+	/**
+	 *  Handle the bundle command's abbreviation.  Invoke bundle()
+	 *
+	 *  @param bundles bundle(s) to display details for
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_BUNDLE_COMMAND_DESCRIPTION)
+	public void b(@Descriptor(ConsoleMsg.CONSOLE_HELP_IDLOCATION_ARGUMENT_DESCRIPTION)Bundle[] bundles) throws Exception {
+		bundle(bundles);
+	}
+
+	/**
+	 *  Handle the bundle command.  Display details for the specified bundle(s).
+	 *
+	 *  @param bundles bundle(s) to display details for
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_BUNDLE_COMMAND_DESCRIPTION)
+	public void bundle(@Descriptor(ConsoleMsg.CONSOLE_HELP_IDLOCATION_ARGUMENT_DESCRIPTION)Bundle[] bundles) throws Exception {
+		if (bundles.length == 0) {
+			System.out.println(ConsoleMsg.CONSOLE_NO_BUNDLE_SPECIFIED_ERROR);
+			return;
+		}
+		
+		for (Bundle bundle : bundles) {
+				long id = bundle.getBundleId();
+				System.out.println(bundle);
+				System.out.print("  "); //$NON-NLS-1$
+				System.out.print(NLS.bind(ConsoleMsg.CONSOLE_ID_MESSAGE, String.valueOf(id)));
+				System.out.print(", "); //$NON-NLS-1$
+				System.out.print(NLS.bind(ConsoleMsg.CONSOLE_STATUS_MESSAGE, getStateName(bundle)));
+				if (id != 0) {
+					File dataRoot = bundle.getDataFile(""); //$NON-NLS-1$
+					String root = (dataRoot == null) ? null : dataRoot.getAbsolutePath();
+					System.out.print(NLS.bind(ConsoleMsg.CONSOLE_DATA_ROOT_MESSAGE, root));
+					System.out.println();
+				} else {
+					System.out.println();
+				}
+
+				ServiceReference<?>[] services = bundle.getRegisteredServices();
+				if (services != null) {
+					System.out.print("  "); //$NON-NLS-1$
+					System.out.println(ConsoleMsg.CONSOLE_REGISTERED_SERVICES_MESSAGE);
+					for (int j = 0; j < services.length; j++) {
+						System.out.print("    "); //$NON-NLS-1$
+						System.out.println(services[j]);
+					}
+				} else {
+					System.out.print("  "); //$NON-NLS-1$
+					System.out.println(ConsoleMsg.CONSOLE_NO_REGISTERED_SERVICES_MESSAGE);
+				}
+
+				services = bundle.getServicesInUse();
+				if (services != null) {
+					System.out.print("  "); //$NON-NLS-1$
+					System.out.println(ConsoleMsg.CONSOLE_SERVICES_IN_USE_MESSAGE);
+					for (int j = 0; j < services.length; j++) {
+						System.out.print("    "); //$NON-NLS-1$
+						System.out.println(services[j]);
+					}
+				} else {
+					System.out.print("  "); //$NON-NLS-1$
+					System.out.println(ConsoleMsg.CONSOLE_NO_SERVICES_IN_USE_MESSAGE);
+				}
+
+				PackageAdmin packageAdmin = activator.getPackageAdmin();
+				if (packageAdmin == null) {
+					System.out.print("  "); //$NON-NLS-1$
+					System.out.println(ConsoleMsg.CONSOLE_NO_EXPORTED_PACKAGES_NO_PACKAGE_ADMIN_MESSAGE);
+					continue;
+				}
+				
+				PlatformAdmin platAdmin = activator.getPlatformAdmin();
+				
+				if (platAdmin != null) {
+					BundleDescription desc = platAdmin.getState(false).getBundle(bundle.getBundleId());
+					if (desc != null) {
+						boolean title = true;
+						
+							ExportPackageDescription[] exports = desc.getExportPackages();
+							if (exports == null || exports.length == 0) {
+								System.out.print("  "); //$NON-NLS-1$
+								System.out.println(ConsoleMsg.CONSOLE_NO_EXPORTED_PACKAGES_MESSAGE);
+							} else {
+								title = true;
+
+								for (int i = 0; i < exports.length; i++) {
+									if (title) {
+										System.out.print("  "); //$NON-NLS-1$
+										System.out.println(ConsoleMsg.CONSOLE_EXPORTED_PACKAGES_MESSAGE);
+										title = false;
+									}
+									System.out.print("    "); //$NON-NLS-1$
+									System.out.print(exports[i].getName());
+									System.out.print("; version=\""); //$NON-NLS-1$
+									System.out.print(exports[i].getVersion());
+									System.out.print("\""); //$NON-NLS-1$
+									if (desc.isRemovalPending()) {
+										System.out.println(ConsoleMsg.CONSOLE_EXPORTED_REMOVAL_PENDING_MESSAGE);
+									} else {
+										System.out.println(ConsoleMsg.CONSOLE_EXPORTED_MESSAGE);
+									}
+								}
+
+								if (title) {
+									System.out.print("  "); //$NON-NLS-1$
+									System.out.println(ConsoleMsg.CONSOLE_NO_EXPORTED_PACKAGES_MESSAGE);
+								}
+							}
+							title = true;
+							if (desc != null) {
+								List<ImportPackageSpecification> fragmentsImportPackages = new ArrayList<ImportPackageSpecification>();
+
+								// Get bundle' fragments imports
+								BundleDescription[] fragments = desc.getFragments();
+								for (int i = 0; i < fragments.length; i++) {
+									ImportPackageSpecification[] fragmentImports = fragments[i].getImportPackages();
+									for (int j = 0; j < fragmentImports.length; j++) {
+										fragmentsImportPackages.add(fragmentImports[j]);
+									}
+								}
+
+								// Get all bundle imports
+								ImportPackageSpecification[] importPackages;
+								if (fragmentsImportPackages.size() > 0) {
+									ImportPackageSpecification[] directImportPackages = desc.getImportPackages();
+									importPackages = new ImportPackageSpecification[directImportPackages.length + fragmentsImportPackages.size()];
+
+									for (int i = 0; i < directImportPackages.length; i++) {
+										importPackages[i] = directImportPackages[i];
+									}
+
+									int offset = directImportPackages.length;
+									for (int i = 0; i < fragmentsImportPackages.size(); i++) {
+										importPackages[offset + i] = fragmentsImportPackages.get(i);
+									}
+								} else {
+									importPackages = desc.getImportPackages();
+								}
+
+								// Get all resolved imports
+								ExportPackageDescription[] imports = null;
+								imports = desc.getContainingState().getStateHelper().getVisiblePackages(desc, StateHelper.VISIBLE_INCLUDE_EE_PACKAGES | StateHelper.VISIBLE_INCLUDE_ALL_HOST_WIRES);
+
+								// Get the unresolved optional and dynamic imports
+								List<ImportPackageSpecification> unresolvedImports = new ArrayList<ImportPackageSpecification>();
+
+								for (int i = 0; i < importPackages.length; i++) {
+									if (importPackages[i].getDirective(Constants.RESOLUTION_DIRECTIVE).equals(ImportPackageSpecification.RESOLUTION_OPTIONAL)) {
+										if (importPackages[i].getSupplier() == null) {
+											unresolvedImports.add(importPackages[i]);
+										}
+									} else if (importPackages[i].getDirective(org.osgi.framework.Constants.RESOLUTION_DIRECTIVE).equals(ImportPackageSpecification.RESOLUTION_DYNAMIC)) {
+										boolean isResolvable = false;
+
+										// Check if the dynamic import can be resolved by any of the wired imports, 
+										// and if not - add it to the list of unresolved imports
+										for (int j = 0; j < imports.length; j++) {
+											if (importPackages[i].isSatisfiedBy(imports[j])) {
+												isResolvable = true;
+											}
+										}
+
+										if (isResolvable == false) {
+											unresolvedImports.add(importPackages[i]);
+										}
+									}
+								}
+
+								title = printImportedPackages(imports, title);
+
+								if (desc.isResolved() && (unresolvedImports.isEmpty() == false)) {
+									printUnwiredDynamicImports(unresolvedImports);
+									title = false;
+								}
+							}
+
+							if (title) {
+								System.out.print("  "); //$NON-NLS-1$
+								System.out.println(ConsoleMsg.CONSOLE_NO_IMPORTED_PACKAGES_MESSAGE);
+							}
+
+							if (packageAdmin != null) {
+								System.out.print("  "); //$NON-NLS-1$
+								if ((packageAdmin.getBundleType(bundle) & PackageAdmin.BUNDLE_TYPE_FRAGMENT) > 0) {
+									org.osgi.framework.Bundle[] hosts = packageAdmin.getHosts(bundle);
+									if (hosts != null) {
+										System.out.println(ConsoleMsg.CONSOLE_HOST_MESSAGE);
+										for (int i = 0; i < hosts.length; i++) {
+											System.out.print("    "); //$NON-NLS-1$
+											System.out.println(hosts[i]);
+										}
+									} else {
+										System.out.println(ConsoleMsg.CONSOLE_NO_HOST_MESSAGE);
+									}
+								} else {
+									org.osgi.framework.Bundle[] fragments = packageAdmin.getFragments(bundle);
+									if (fragments != null) {
+										System.out.println(ConsoleMsg.CONSOLE_FRAGMENT_MESSAGE);
+										for (int i = 0; i < fragments.length; i++) {
+											System.out.print("    "); //$NON-NLS-1$
+											System.out.println(fragments[i]);
+										}
+									} else {
+										System.out.println(ConsoleMsg.CONSOLE_NO_FRAGMENT_MESSAGE);
+									}
+								}
+
+								RequiredBundle[] requiredBundles = packageAdmin.getRequiredBundles(null);
+								RequiredBundle requiredBundle = null;
+								if (requiredBundles != null) {
+									for (RequiredBundle rb : requiredBundles) {
+										if (rb.getBundle() == bundle) {
+											requiredBundle = rb;
+											break;
+										}
+									}
+								}
+
+								if (requiredBundle == null) {
+									System.out.print("  "); //$NON-NLS-1$
+									System.out.println(ConsoleMsg.CONSOLE_NO_NAMED_CLASS_SPACES_MESSAGE);
+								} else {
+									System.out.print("  "); //$NON-NLS-1$
+									System.out.println(ConsoleMsg.CONSOLE_NAMED_CLASS_SPACE_MESSAGE);
+									System.out.print("    "); //$NON-NLS-1$
+									System.out.print(requiredBundle);
+									if (requiredBundle.isRemovalPending()) {
+										System.out.println(ConsoleMsg.CONSOLE_REMOVAL_PENDING_MESSAGE);
+									} else {
+										System.out.println(ConsoleMsg.CONSOLE_PROVIDED_MESSAGE);
+									}
+								}
+								title = true;
+								if (requiredBundles != null) {
+									for (RequiredBundle rb : requiredBundles) {
+										if (rb == requiredBundle)
+											continue;
+
+										org.osgi.framework.Bundle[] depBundles = rb.getRequiringBundles();
+										if (depBundles == null)
+											continue;
+
+										for (int j = 0; j < depBundles.length; j++) {
+											if (depBundles[j] == bundle) {
+												if (title) {
+													System.out.print("  "); //$NON-NLS-1$
+													System.out
+															.println(ConsoleMsg.CONSOLE_REQUIRED_BUNDLES_MESSAGE);
+													title = false;
+												}
+												System.out.print("    "); //$NON-NLS-1$
+												System.out.print(rb);
+
+												org.osgi.framework.Bundle provider = rb.getBundle();
+												System.out.print("<"); //$NON-NLS-1$
+												System.out.print(provider);
+												System.out.println(">"); //$NON-NLS-1$
+											}
+										}
+									}
+								}
+								if (title) {
+									System.out.print("  "); //$NON-NLS-1$
+									System.out.println(ConsoleMsg.CONSOLE_NO_REQUIRED_BUNDLES_MESSAGE);
+								}
+
+							}
+						} 
+					System.out.println();
+					System.out.println();
+				} else {
+					System.out.print("  "); //$NON-NLS-1$
+					System.out.println(ConsoleMsg.CONSOLE_NO_EXPORTED_PACKAGES_NO_PLATFORM_ADMIN_MESSAGE);
+				}
+				
+				SecurityManager sm = System.getSecurityManager();
+				if (sm != null) {
+					ProtectionDomain domain = ((AbstractBundle)bundle).getProtectionDomain();
+					System.out.println(domain);
+				}
+		}
+	}
+
+	private boolean printImportedPackages(ExportPackageDescription[] importedPkgs, boolean title) {
+		for (int i = 0; i < importedPkgs.length; i++) {
+			if (title) {
+				System.out.print("  "); //$NON-NLS-1$
+				System.out.println(ConsoleMsg.CONSOLE_IMPORTED_PACKAGES_MESSAGE);
+				title = false;
+			}
+			System.out.print("    "); //$NON-NLS-1$
+			System.out.print(importedPkgs[i].getName());
+			System.out.print("; version=\""); //$NON-NLS-1$
+			System.out.print(importedPkgs[i].getVersion());
+			System.out.print("\""); //$NON-NLS-1$
+			Bundle exporter = context.getBundle(importedPkgs[i].getSupplier().getBundleId());
+			if (exporter != null) {
+				System.out.print("<"); //$NON-NLS-1$
+				System.out.print(exporter);
+				System.out.println(">"); //$NON-NLS-1$
+			} else {
+				System.out.print("<"); //$NON-NLS-1$
+				System.out.print(ConsoleMsg.CONSOLE_STALE_MESSAGE);
+				System.out.println(">"); //$NON-NLS-1$
+			}
+		}
+		return title;
+	}
+
+	private void printUnwiredDynamicImports(List<ImportPackageSpecification> dynamicImports) {
+		for (int i = 0; i < dynamicImports.size(); i++) {
+			ImportPackageSpecification importPackage = dynamicImports.get(i);
+			System.out.print("    "); //$NON-NLS-1$
+			System.out.print(importPackage.getName());
+			System.out.print("; version=\""); //$NON-NLS-1$
+			System.out.print(importPackage.getVersionRange());
+			System.out.print("\""); //$NON-NLS-1$
+			System.out.print("<"); //$NON-NLS-1$
+			System.out.print("unwired"); //$NON-NLS-1$
+			System.out.print(">"); //$NON-NLS-1$
+			System.out.print("<"); //$NON-NLS-1$
+			System.out.print(importPackage.getDirective(org.osgi.framework.Constants.RESOLUTION_DIRECTIVE));
+			System.out.println(">"); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 *  Handle the gc command.  Perform a garbage collection.
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_GC_COMMAND_DESCRIPTION)
+	public void gc() throws Exception {
+		long before = Runtime.getRuntime().freeMemory();
+
+		/* Let the finilizer finish its work and remove objects from its queue */
+		System.gc(); /* asyncronous garbage collector might already run */
+		System.gc(); /* to make sure it does a full gc call it twice */
+		System.runFinalization();
+		try {
+			Thread.sleep(100);
+		} catch (InterruptedException e) {
+			// do nothing
+		}
+
+		long after = Runtime.getRuntime().freeMemory();
+		System.out.print(ConsoleMsg.CONSOLE_TOTAL_MEMORY_MESSAGE);
+		System.out.println(String.valueOf(Runtime.getRuntime().totalMemory()));
+		System.out.print(ConsoleMsg.CONSOLE_FREE_MEMORY_BEFORE_GARBAGE_COLLECTION_MESSAGE);
+		System.out.println(String.valueOf(before));
+		System.out.print(ConsoleMsg.CONSOLE_FREE_MEMORY_AFTER_GARBAGE_COLLECTION_MESSAGE);
+		System.out.println(String.valueOf(after));
+		System.out.print(ConsoleMsg.CONSOLE_MEMORY_GAINED_WITH_GARBAGE_COLLECTION_MESSAGE);
+		System.out.println(String.valueOf(after - before));
+	}
+
+	/**
+	 *  Handle the init command.  Uninstall all bundles.
+	 *
+	 */
+	@SuppressWarnings("deprecation")
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_INIT_COMMAND_DESCRIPTION)
+	public void init() throws Exception {
+		if (context.getBundle(0).getState() == Bundle.ACTIVE) {
+			System.out.print(newline);
+			System.out.println(ConsoleMsg.CONSOLE_FRAMEWORK_LAUNCHED_PLEASE_SHUTDOWN_MESSAGE);
+			return;
+		}
+
+		Bundle[] bundles = context.getBundles();
+
+		int size = bundles.length;
+
+		if (size > 0) {
+			for (int i = 0; i < size; i++) {
+				Bundle bundle = bundles[i];
+
+				if (bundle.getBundleId() != 0) {
+					try {
+						bundle.uninstall();
+					} catch (BundleException e) {
+						e.printStackTrace();
+					}
+				}
+			}
+		} else {
+			System.out.println(ConsoleMsg.CONSOLE_NO_INSTALLED_BUNDLES_ERROR);
+		}
+		PermissionAdmin securityAdmin = activator.getPermissionAdmin();
+		ConditionalPermissionAdmin condPermAdmin = activator.getConditionalPermissionAdmin();
+		if (securityAdmin != null) {
+			// clear the permissions from permission admin
+			securityAdmin.setDefaultPermissions(null);
+			String[] permLocations = securityAdmin.getLocations();
+			if (permLocations != null)
+				for (int i = 0; i < permLocations.length; i++)
+					securityAdmin.setPermissions(permLocations[i], null);
+			ConditionalPermissionUpdate update = condPermAdmin.newConditionalPermissionUpdate();
+			update.getConditionalPermissionInfos().clear();
+			update.commit();
+		}
+		// clear the permissions from conditional permission admin
+		if (securityAdmin != null)
+			for (Enumeration<ConditionalPermissionInfo> infos = condPermAdmin.getConditionalPermissionInfos(); infos.hasMoreElements();)
+				infos.nextElement().delete();
+	}
+
+	/**
+	 *  Handle the close command.  Shutdown and exit.
+	
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_CLOSE_COMMAND_DESCRIPTION)
+	public void close() throws Exception {
+		context.getBundle(0).stop();
+		System.exit(0);
+	}
+
+	/**
+	 *  Handle the refresh command's abbreviation.  Invoke refresh()
+	 *
+	 *  @param bundles bundle(s) to be refreshed
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_REFRESH_COMMAND_DESCRIPTION)
+	public void r(@Descriptor(ConsoleMsg.CONSOLE_HELP_REFRESH_COMMAND_ARGUMENT_DESCRIPTION) Bundle... bundles) throws Exception {
+		refresh(bundles);
+	}
+
+	/**
+	 *  Handle the refresh command.  Refresh the packages of the specified bundles.
+	 *
+	 *  @param bundles bundle(s) to be refreshed
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_REFRESH_COMMAND_DESCRIPTION)
+	public void refresh(@Descriptor(ConsoleMsg.CONSOLE_HELP_REFRESH_COMMAND_ARGUMENT_DESCRIPTION) Bundle... bundles) throws Exception {
+		PackageAdmin packageAdmin = activator.getPackageAdmin();
+		if (packageAdmin != null) {
+			if(bundles != null && bundles.length > 0) {
+				packageAdmin.refreshPackages(bundles);
+			} else {
+				packageAdmin.refreshPackages(null);
+			}
+		} else {
+			System.out.println(ConsoleMsg.CONSOLE_CAN_NOT_REFRESH_NO_PACKAGE_ADMIN_ERROR);
+		}
+	}
+
+	/**
+	 * Executes the given system command in a separate system process
+	 * and waits for it to finish.
+	 *
+	 * @param command command to be executed
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_EXEC_COMMAND_DESCRIPTION)
+	public void exec(@Descriptor(ConsoleMsg.CONSOLE_HELP_EXEC_COMMAND_ARGUMENT_DESCRIPTION) String command) throws Exception {
+		if (command == null) {
+			System.out.println(ConsoleMsg.CONSOLE_NO_COMMAND_SPECIFIED_ERROR);
+			return;
+		}
+
+		Process p = Runtime.getRuntime().exec(command);
+
+		System.out.println(NLS.bind(ConsoleMsg.CONSOLE_STARTED_IN_MESSAGE, command, String.valueOf(p)));
+		int result = p.waitFor();
+		System.out.println(NLS.bind(ConsoleMsg.CONSOLE_EXECUTED_RESULT_CODE_MESSAGE, command, String.valueOf(result)));
+	}
+
+	/**
+	 * Executes the given system command in a separate system process.  It does
+	 * not wait for a result.
+	 *
+	 * @param command command to be executed
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_FORK_COMMAND_DESCRIPTION)
+	public void fork(@Descriptor(ConsoleMsg.CONSOLE_HELP_FORK_COMMAND_ARGUMENT_DESCRIPTION) String command) throws Exception {
+		if (command == null) {
+			System.out.println(ConsoleMsg.CONSOLE_NO_COMMAND_SPECIFIED_ERROR);
+			return;
+		}
+
+		Process p = Runtime.getRuntime().exec(command);
+		System.out.println(NLS.bind(ConsoleMsg.CONSOLE_STARTED_IN_MESSAGE, command, String.valueOf(p)));
+	}
+
+	/**
+	 * Handle the headers command's abbreviation.  Invoke headers()
+	 *
+	 * @param bundles bundle(s) whose headers to display
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_HEADERS_COMMAND_DESCRIPTION)
+	public List<Dictionary<String, String>> h(@Descriptor(ConsoleMsg.CONSOLE_HELP_HEADERS_COMMAND_ARGUMENT_DESCRIPTION) Bundle... bundles) throws Exception {
+		return headers(bundles);
+	}
+
+	/**
+	 * Handle the headers command.  Display headers for the specified bundle(s).
+	 *
+	 * @param bundles bundle(s) whose headers to display
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_HEADERS_COMMAND_DESCRIPTION)
+	public List<Dictionary<String, String>> headers(@Descriptor(ConsoleMsg.CONSOLE_HELP_HEADERS_COMMAND_ARGUMENT_DESCRIPTION) Bundle... bundles) throws Exception {
+		ArrayList<Dictionary<String, String>> headers = new ArrayList<Dictionary<String,String>>();
+		
+		if (bundles == null || bundles.length == 0) {
+			System.out.println(ConsoleMsg.CONSOLE_NO_BUNDLE_SPECIFIED_ERROR);
+			return headers;
+		}
+		
+		
+		for (Bundle bundle : bundles) {
+			headers.add(((AbstractBundle)bundle).getHeaders());
+		}
+		return headers;
+	}
+	
+	/**
+	 * Handles the props command's abbreviation.  Invokes props()
+	 *
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_PROPS_COMMAND_DESCRIPTION)
+	public Dictionary<?, ?> pr() throws Exception {
+		 return props();
+	}
+
+	/**
+	 * Handles the _props command.  Prints the system properties sorted.
+	 *
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_PROPS_COMMAND_DESCRIPTION)
+	public Dictionary<?, ?> props() throws Exception {
+		System.out.println(ConsoleMsg.CONSOLE_SYSTEM_PROPERTIES_TITLE);
+		return System.getProperties();
+	}
+
+	/**
+	 * Handles the setprop command's abbreviation.  Invokes setprop()
+	 *
+	 * @param arguments key=value pairs for the new properties
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_SETPROP_COMMAND_DESCRIPTION)
+	public void setp(@Descriptor(ConsoleMsg.CONSOLE_HELP_SETPROP_COMMAND_ARGUMENTS_DESCRIPTION) String[] arguments) throws Exception {
+		setprop(arguments);
+	}
+
+	/**
+	 * Handles the setprop command.  Sets the CDS property in the given argument.
+	 *
+	 * @param arguments key=value pairs for the new properties
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_SETPROP_COMMAND_DESCRIPTION)
+	public void setprop(@Descriptor(ConsoleMsg.CONSOLE_HELP_SETPROP_COMMAND_ARGUMENTS_DESCRIPTION) String[] arguments) throws Exception {
+		if (arguments == null) {
+			System.out.println(ConsoleMsg.CONSOLE_NO_PARAMETERS_SPECIFIED_TITLE);
+			props();
+		} else {
+			ServiceReference envInfoRef = context.getServiceReference(EnvironmentInfo.class.getName());
+			if (envInfoRef != null) {
+				// EnvironmentInfo is used because FrameworkProperties cannot be directly accessed outside of the system bundle
+				EnvironmentInfo envInfo = context.getService(envInfoRef);
+				if (envInfo != null) {
+					System.out.println(ConsoleMsg.CONSOLE_SETTING_PROPERTIES_TITLE);
+					for(String argument : arguments) {
+						int index = argument.indexOf("=");
+						if(index > -1) {
+							String key = argument.substring(0, index);
+							String value = argument.substring(index + 1, argument.length());
+							envInfo.setProperty(key, value);
+							System.out.println(tab + key + " = " + value); //$NON-NLS-1$
+						}
+					}
+				} else {
+					System.out.print("  "); //$NON-NLS-1$
+					System.out.println(ConsoleMsg.CONSOLE_CANNOT_ACCESS_SYSTEM_PROPERTIES);
+				}
+			}
+		}
+	}
+
+	/**
+	 * Prints the short version of the status.
+	 * For the long version use "status".
+	 *
+	 * @param arguments
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_SS_COMMAND_DESCRIPTION)
+	public void ss(@Descriptor(ConsoleMsg.CONSOLE_HELP_STATUS_ARGUMENT_DESCRIPTION) String... arguments) throws Exception {
+		if (context.getBundle(0).getState() == Bundle.ACTIVE) {
+			System.out.println(ConsoleMsg.CONSOLE_FRAMEWORK_IS_LAUNCHED_MESSAGE);
+		} else {
+			System.out.println(ConsoleMsg.CONSOLE_FRAMEWORK_IS_SHUTDOWN_MESSAGE);
+		}
+		System.out.println();
+		
+		String states = "";
+		String[] bsnSegments = null;
+		
+		if(arguments != null && arguments.length > 0) {
+			if(arguments[0].equals("-s")) {
+				if (arguments.length > 1) {
+					states = arguments[1];
+					if(arguments.length > 2) {
+						bsnSegments = new String[arguments.length - 2];
+						System.arraycopy(arguments, 2, bsnSegments, 0, bsnSegments.length);
+					}
+				}
+			} else {
+				bsnSegments = arguments;
+			}
+		}
+
+		int stateFilter;
+		
+		try {
+			stateFilter = getStatesFromConstants(states);
+		} catch (IllegalArgumentException e) {
+			return;
+		}
+		
+		Bundle[] bundles = context.getBundles();
+		int size = bundles.length;
+
+		if (size == 0) {
+			System.out.println(ConsoleMsg.CONSOLE_NO_INSTALLED_BUNDLES_ERROR);
+			return;
+		} else {
+			System.out.print(newline);
+			System.out.print(ConsoleMsg.CONSOLE_ID);
+			System.out.print(tab);
+			System.out.println(ConsoleMsg.CONSOLE_STATE_BUNDLE_TITLE);
+			for (Bundle b : bundles) {
+				
+				if (!match(b, bsnSegments, stateFilter))
+					continue;
+				String label = b.getSymbolicName();
+				if (label == null || label.length() == 0)
+					label = b.toString();
+				else
+					label = label + "_" + b.getVersion(); //$NON-NLS-1$
+				System.out.println(b.getBundleId() + "\t" + getStateName(b) + label); //$NON-NLS-1$ 
+				PackageAdmin packageAdmin = activator.getPackageAdmin();
+				if ((packageAdmin.getBundleType(b) & PackageAdmin.BUNDLE_TYPE_FRAGMENT) != 0) {
+					Bundle[] hosts = packageAdmin.getHosts(b);
+					if (hosts != null)
+						for (int j = 0; j < hosts.length; j++)
+							System.out.println("\t            Master=" + hosts[j].getBundleId()); //$NON-NLS-1$
+				} else {
+					Bundle[] fragments = packageAdmin.getFragments(b);
+					if (fragments != null) {
+						System.out.print("\t            Fragments="); //$NON-NLS-1$
+						for (int f = 0; f < fragments.length; f++) {
+							Bundle fragment = fragments[f];
+							System.out.print((f > 0 ? ", " : "") + fragment.getBundleId()); //$NON-NLS-1$ //$NON-NLS-2$
+						}
+						System.out.println();
+					}
+				}
+			}
+		}
+	}
+
+	private boolean match(Bundle toFilter, String[] searchedName, int searchedState) {
+		if ((toFilter.getState() & searchedState) == 0) {
+			return false;
+		}
+		if (searchedName != null && searchedName.length > 0 && toFilter.getSymbolicName() != null && toFilter.getSymbolicName().indexOf(searchedName[0]) == -1) {
+			return false;
+		}
+		return true;
+	}
+
+	/**
+	 * Handles the threads command abbreviation.  Invokes threads().
+	 *
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_THREADS_COMMAND_DESCRIPTION)
+	public void t() throws Exception {
+		threads();
+	}
+
+	/**
+	 * Prints the information about the currently running threads
+	 * in the embedded system.
+	 *
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_THREADS_COMMAND_DESCRIPTION)
+	public void threads() throws Exception {
+
+		ThreadGroup[] threadGroups = getThreadGroups();
+		Util.sortByString(threadGroups);
+
+		ThreadGroup tg = getTopThreadGroup();
+		Thread[] threads = new Thread[tg.activeCount()];
+		int count = tg.enumerate(threads, true);
+		Util.sortByString(threads);
+
+		StringBuffer sb = new StringBuffer(120);
+		System.out.println();
+		System.out.println(ConsoleMsg.CONSOLE_THREADGROUP_TITLE);
+		for (int i = 0; i < threadGroups.length; i++) {
+			tg = threadGroups[i];
+			int all = tg.activeCount(); //tg.allThreadsCount();
+			int local = tg.enumerate(new Thread[all], false); //tg.threadsCount();
+			ThreadGroup p = tg.getParent();
+			String parent = (p == null) ? "-none-" : p.getName(); //$NON-NLS-1$
+			sb.setLength(0);
+			sb.append(Util.toString(simpleClassName(tg), 18)).append(" ").append(Util.toString(tg.getName(), 21)).append(" ").append(Util.toString(parent, 16)).append(Util.toString(Integer.valueOf(tg.getMaxPriority()), 3)).append(Util.toString(Integer.valueOf(local), 4)).append("/").append(Util.toString(String.valueOf(all), 6)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+			System.out.println(sb.toString());
+		}
+		System.out.print(newline);
+		System.out.println(ConsoleMsg.CONSOLE_THREADTYPE_TITLE);
+		for (int j = 0; j < count; j++) {
+			Thread t = threads[j];
+			if (t != null) {
+				sb.setLength(0);
+				sb.append(Util.toString(simpleClassName(t), 18)).append(" ").append(Util.toString(t.getName(), 21)).append(" ").append(Util.toString(t.getThreadGroup().getName(), 16)).append(Util.toString(Integer.valueOf(t.getPriority()), 3)); //$NON-NLS-1$ //$NON-NLS-2$
+				if (t.isDaemon())
+					sb.append(" [daemon]"); //$NON-NLS-1$
+				System.out.println(sb.toString());
+			}
+		}
+	}
+
+	/**
+	 * Handles the sl (startlevel) command. 
+	 *
+	 * @param bundle bundle to display startlevel for; if no bundle is specified, the framework startlevel is displayed
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_SL_COMMAND_DESCRIPTION)
+	public void sl(@Descriptor(ConsoleMsg.CONSOLE_HELP_SL_COMMAND_ARGUMENT_DESCRIPTION) Bundle... bundle) throws Exception {
+		StartLevel startLevel = activator.getStartLevel();
+		if (startLevel != null) {
+			int value = 0;
+			if (bundle == null || bundle.length == 0) { // must want framework startlevel
+				value = startLevel.getStartLevel();
+				System.out.println(NLS.bind(ConsoleMsg.STARTLEVEL_FRAMEWORK_ACTIVE_STARTLEVEL, String.valueOf(value)));
+			} else { // must want bundle startlevel
+				value = startLevel.getBundleStartLevel(bundle[0]);
+				System.out.println(NLS.bind(ConsoleMsg.STARTLEVEL_BUNDLE_STARTLEVEL, Long.valueOf(bundle[0].getBundleId()), Integer.valueOf(value)));
+			}
+		}
+	}
+
+	/**
+	 * Handles the setfwsl (set framework startlevel) command. 
+	 *
+	 * @param newSL new value for the framewrok start level
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_SETFWSL_COMMAND_DESCRIPTION)
+	public void setfwsl(@Descriptor(ConsoleMsg.CONSOLE_HELP_SETFWSL_COMMAND_ARGUMENT_DESCRIPTION) int newSL) throws Exception {
+		StartLevel startLevel = activator.getStartLevel();
+		if (startLevel != null) {
+			try {
+				startLevel.setStartLevel(newSL);
+				System.out.println(NLS.bind(ConsoleMsg.STARTLEVEL_FRAMEWORK_ACTIVE_STARTLEVEL, String.valueOf(newSL)));
+			} catch (IllegalArgumentException e) {
+				System.out.println(e.getMessage());
+			}
+		}
+	}
+
+	/**
+	 * Handles the setbsl (set bundle startlevel) command. 
+	 *
+	 * @param newSL new value for bundle start level
+	 * @param bundles bundles whose start value will be changed
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_SETBSL_COMMAND_DESCRIPTION)
+	public void setbsl(
+			@Descriptor(ConsoleMsg.CONSOLE_HELP_SETFWSL_COMMAND_ARGUMENT_DESCRIPTION)int newSL, 
+			@Descriptor(ConsoleMsg.CONSOLE_HELP_SETBSL_COMMAND_ARGUMENT_DESCRIPTION) Bundle... bundles) throws Exception {
+		StartLevel startLevel = activator.getStartLevel();
+		if (startLevel != null) {
+			if (bundles == null) {
+				System.out.println(ConsoleMsg.STARTLEVEL_NO_STARTLEVEL_OR_BUNDLE_GIVEN);
+				return;
+			}
+			for (Bundle bundle : bundles) {	
+				try {
+					startLevel.setBundleStartLevel(bundle, newSL);
+					System.out.println(NLS.bind(ConsoleMsg.STARTLEVEL_BUNDLE_STARTLEVEL, Long.valueOf(bundle.getBundleId()), Integer.valueOf(newSL)));
+				} catch (IllegalArgumentException e) {
+					System.out.println(e.getMessage());
+				}
+			}
+		}
+	}
+
+	/**
+	 * Handles the setibsl (set initial bundle startlevel) command. 
+	 *
+	 * @param newInitialSL new value for initial start level
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_SETIBSL_COMMAND_DESCRIPTION)
+	public void setibsl(@Descriptor(ConsoleMsg.CONSOLE_HELP_SETFWSL_COMMAND_ARGUMENT_DESCRIPTION) int newInitialSL) throws Exception {
+		StartLevel startLevel = activator.getStartLevel();
+		if (startLevel != null) {
+			try {
+				startLevel.setInitialBundleStartLevel(newInitialSL);
+				System.out.println(NLS.bind(ConsoleMsg.STARTLEVEL_INITIAL_BUNDLE_STARTLEVEL, String.valueOf(newInitialSL)));
+			} catch (IllegalArgumentException e) {
+				System.out.println(e.getMessage());
+			}
+		}
+	}
+
+	/**
+	 * Lists required bundles having the specified symbolic name or all if no bsn is specified
+	 * 
+	 * @param symbolicName
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_REQUIRED_BUNDLES_COMMAND_DESCRIPTION)
+	public void requiredBundles(@Descriptor(ConsoleMsg.CONSOLE_HELP_REQUIRED_BUNDLES_COMMAND_ARGUMENT_DESCRIPTION) String... symbolicName) {
+		classSpaces(symbolicName);
+	}
+
+	/**
+	 * Lists required bundles having the specified symbolic name or all if no bsn is specified
+	 * 
+	 * @param symbolicName
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_REQUIRED_BUNDLES_COMMAND_DESCRIPTION)
+	public void classSpaces(@Descriptor(ConsoleMsg.CONSOLE_HELP_REQUIRED_BUNDLES_COMMAND_ARGUMENT_DESCRIPTION) String... symbolicName) {
+		PackageAdmin packageAdmin = activator.getPackageAdmin();
+		if (packageAdmin != null) {
+			RequiredBundle[] symBundles = null;
+			String name;
+			if(symbolicName == null || symbolicName.length == 0) {
+				name = null;
+			} else {
+				name = symbolicName[0];
+			}
+			symBundles = packageAdmin.getRequiredBundles(name);
+
+			if (symBundles == null) {
+				System.out.println(ConsoleMsg.CONSOLE_NO_NAMED_CLASS_SPACES_MESSAGE);
+			} else {
+				for (RequiredBundle symBundle : symBundles) {
+
+					System.out.print(symBundle);
+
+					boolean removalPending = symBundle.isRemovalPending();
+					if (removalPending) {
+						System.out.print("("); //$NON-NLS-1$
+						System.out.print(ConsoleMsg.CONSOLE_REMOVAL_PENDING_MESSAGE);
+						System.out.println(")"); //$NON-NLS-1$
+					}
+
+					Bundle provider = symBundle.getBundle();
+					if (provider != null) {
+						System.out.print("<"); //$NON-NLS-1$
+						System.out.print(provider);
+						System.out.println(">"); //$NON-NLS-1$
+
+						Bundle[] requiring = symBundle.getRequiringBundles();
+						if (requiring != null)
+							for (int j = 0; j < requiring.length; j++) {
+								System.out.print("  "); //$NON-NLS-1$
+								System.out.print(requiring[j]);
+								System.out.print(" "); //$NON-NLS-1$
+								System.out.println(ConsoleMsg.CONSOLE_REQUIRES_MESSAGE);
+							}
+					} else {
+						System.out.print("<"); //$NON-NLS-1$
+						System.out.print(ConsoleMsg.CONSOLE_STALE_MESSAGE);
+						System.out.println(">"); //$NON-NLS-1$
+					}
+
+				}
+			}
+		} else {
+			System.out.println(ConsoleMsg.CONSOLE_NO_EXPORTED_PACKAGES_NO_PACKAGE_ADMIN_MESSAGE);
+		}
+	}
+
+	/**
+	 * Handles the profilelog command. 
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_PROFILELOG_COMMAND_DESCRIPTION)
+	public void profilelog() throws Exception {
+		System.out.println(Profile.getProfileLog());
+	}
+
+	/**
+	 * Lists all packages visible from the specified bundle
+	 * @param bundle bundle to list visible packages
+	 */
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_VISIBLE_PACKAGES_COMMAND_DESCRIPTION)
+	public void getPackages(@Descriptor(ConsoleMsg.CONSOLE_HELP_VISIBLE_PACKAGES_COMMAND_ARGUMENTS_DESCRIPTION) Bundle bundle) {
+		PlatformAdmin platformAdmin = activator.getPlatformAdmin();
+		if (platformAdmin == null)
+			return;
+			BundleDescription bundleDescription = platformAdmin.getState(false).getBundle(bundle.getBundleId());
+			ExportPackageDescription[] exports = platformAdmin.getStateHelper().getVisiblePackages(bundleDescription, StateHelper.VISIBLE_INCLUDE_EE_PACKAGES | StateHelper.VISIBLE_INCLUDE_ALL_HOST_WIRES);
+			for (int i = 0; i < exports.length; i++) {
+				System.out.println(exports[i] + ": " + platformAdmin.getStateHelper().getAccessCode(bundleDescription, exports[i])); //$NON-NLS-1$
+			}
+	}
+
+	/**
+	 *  Given a string containing a startlevel value, validate it and convert it to an int
+	 * 
+	 *  @param intp A CommandInterpreter object used for printing out error messages
+	 *  @param value A string containing a potential startlevel
+	 *  @return The start level or an int <0 if it was invalid
+	 */
+	protected int getStartLevelFromToken(String value) {
+		int retval = -1;
+		try {
+			retval = Integer.parseInt(value);
+			if (Integer.parseInt(value) <= 0) {
+				System.out.println(ConsoleMsg.STARTLEVEL_POSITIVE_INTEGER);
+			}
+		} catch (NumberFormatException nfe) {
+			System.out.println(ConsoleMsg.STARTLEVEL_POSITIVE_INTEGER);
+		}
+		return retval;
+	}
+
+	/**
+	 *  Given a bundle, return the string describing that bundle's state.
+	 *
+	 *  @param bundle A bundle to return the state of
+	 *  @return A String describing the state
+	 */
+	protected String getStateName(Bundle bundle) {
+		int state = bundle.getState();
+		switch (state) {
+			case Bundle.UNINSTALLED :
+				return "UNINSTALLED "; //$NON-NLS-1$
+
+			case Bundle.INSTALLED :
+				if (isDisabled(bundle)) {
+					return "<DISABLED>  "; //$NON-NLS-1$	
+				}
+				return "INSTALLED   "; //$NON-NLS-1$
+
+			case Bundle.RESOLVED :
+				return "RESOLVED    "; //$NON-NLS-1$
+
+			case Bundle.STARTING :
+				synchronized (lazyActivation) {
+					if (lazyActivation.contains(bundle)) {
+						return "<<LAZY>>    "; //$NON-NLS-1$
+					}
+					return "STARTING    "; //$NON-NLS-1$
+				}
+
+			case Bundle.STOPPING :
+				return "STOPPING    "; //$NON-NLS-1$
+
+			case Bundle.ACTIVE :
+				return "ACTIVE      "; //$NON-NLS-1$
+
+			default :
+				return Integer.toHexString(state);
+		}
+	}
+
+	private boolean isDisabled(Bundle bundle) {
+		boolean disabled = false;
+		ServiceReference<?> platformAdminRef = null;
+		try {
+			platformAdminRef = context.getServiceReference(PlatformAdmin.class.getName());
+			if (platformAdminRef != null) {
+				PlatformAdmin platAdmin = (PlatformAdmin) context.getService(platformAdminRef);
+				if (platAdmin != null) {
+					State state = platAdmin.getState(false);
+					BundleDescription bundleDesc = state.getBundle(bundle.getBundleId());
+					DisabledInfo[] disabledInfos = state.getDisabledInfos(bundleDesc);
+					if ((disabledInfos != null) && (disabledInfos.length != 0)) {
+						disabled = true;
+					}
+				}
+			}
+		} finally {
+			if (platformAdminRef != null)
+				context.ungetService(platformAdminRef);
+		}
+		return disabled;
+	}
+
+	/**
+	 * Answers all thread groups in the system.
+	 *
+	 * @return	An array of all thread groups.
+	 */
+	protected ThreadGroup[] getThreadGroups() {
+		ThreadGroup tg = getTopThreadGroup();
+		ThreadGroup[] groups = new ThreadGroup[tg.activeGroupCount()];
+		int count = tg.enumerate(groups, true);
+		if (count == groups.length) {
+			return groups;
+		}
+		// get rid of null entries
+		ThreadGroup[] ngroups = new ThreadGroup[count];
+		System.arraycopy(groups, 0, ngroups, 0, count);
+		return ngroups;
+	}
+
+	/**
+	 * Answers the top level group of the current thread.
+	 * <p>
+	 * It is the 'system' or 'main' thread group under
+	 * which all 'user' thread groups are allocated.
+	 *
+	 * @return	The parent of all user thread groups.
+	 */
+	protected ThreadGroup getTopThreadGroup() {
+		ThreadGroup topGroup = Thread.currentThread().getThreadGroup();
+		if (topGroup != null) {
+			while (topGroup.getParent() != null) {
+				topGroup = topGroup.getParent();
+			}
+		}
+		return topGroup;
+	}
+
+	/**
+	 * Returns the simple class name of an object.
+	 *
+	 * @param o The object for which a class name is requested
+	 * @return	The simple class name.
+	 */
+	public String simpleClassName(Object o) {
+		java.util.StringTokenizer t = new java.util.StringTokenizer(o.getClass().getName(), "."); //$NON-NLS-1$
+		int ct = t.countTokens();
+		for (int i = 1; i < ct; i++) {
+			t.nextToken();
+		}
+		return t.nextToken();
+	}
+
+	@Descriptor(ConsoleMsg.CONSOLE_HELP_GETPROP_COMMAND_DESCRIPTION)
+	public void getprop(@Descriptor(ConsoleMsg.CONSOLE_HELP_GETPROP_COMMAND_ARGUMENT_DESCRIPTION) String... propName) throws Exception {
+		Properties allProperties = System.getProperties();
+		Iterator<?> propertyNames = new TreeSet<Object>(allProperties.keySet()).iterator();
+		while (propertyNames.hasNext()) {
+			String prop = (String) propertyNames.next();
+			if (propName == null || propName.length == 0 || prop.startsWith(propName[0])) {
+				System.out.println(prop + '=' + allProperties.getProperty(prop));
+			}
+		}
+	}
+
+	/**
+	 * This is used to track lazily activated bundles.
+	 */
+	public void bundleChanged(BundleEvent event) {
+		int type = event.getType();
+		Bundle bundle = event.getBundle();
+		synchronized (lazyActivation) {
+			switch (type) {
+				case BundleEvent.LAZY_ACTIVATION :
+					if (!lazyActivation.contains(bundle)) {
+						lazyActivation.add(bundle);
+					}
+					break;
+
+				default :
+					lazyActivation.remove(bundle);
+					break;
+			}
+		}
+
+	}
+}
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/commands/EquinoxCommandsConverter.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/commands/EquinoxCommandsConverter.java
new file mode 100644
index 0000000..eb34385
--- /dev/null
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/commands/EquinoxCommandsConverter.java
@@ -0,0 +1,152 @@
+/*******************************************************************************
+ * 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.commands;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.List;
+
+import org.apache.felix.service.command.Converter;
+import org.eclipse.osgi.framework.internal.core.Util;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Version;
+
+/**
+ * Converter for the arguments of the migrated equinox commands.
+ */
+public class EquinoxCommandsConverter implements Converter {
+	BundleContext context;
+	
+	public EquinoxCommandsConverter(BundleContext context) {
+		this.context = context;
+	}
+	
+	public Object convert(Class<?> desiredType, Object in) throws Exception {
+		if(desiredType == Bundle[].class) {
+			if (in instanceof String) {
+				if("*".equals((String) in)) {
+					return context.getBundles();
+				}
+			} else if (in instanceof List) {
+				try {
+					List<String> args = (List<String>) in;
+					ArrayList<Bundle> bundles = new ArrayList<Bundle>();
+					for(String arg : args) {
+						long id = Long.parseLong(arg);
+						bundles.add(context.getBundle(id));
+					}
+					return bundles.toArray(new Bundle[0]);
+				} catch (Exception e) {
+					return null;
+				}
+			}
+		}
+		
+		if(desiredType == Bundle.class) {
+			Bundle bundle = null;
+			try {
+				long id = Long.parseLong((String) in);
+				bundle = context.getBundle(id);
+			} catch (NumberFormatException nfe) {
+
+				// if not found, assume token is either symbolic name@version, or location
+				String symbolicName = (String) in;
+				Version version = null;
+
+				// check for @ -- this may separate either the version string, or be part of the
+				// location
+				int ix = symbolicName.indexOf("@"); //$NON-NLS-1$
+				if (ix != -1) {
+					if ((ix + 1) != symbolicName.length()) {
+						try {
+							// if the version parses, then use the token prior to @ as a symbolic name
+							version = Version.parseVersion(symbolicName.substring(ix + 1, symbolicName.length()));
+							symbolicName = symbolicName.substring(0, ix);
+						} catch (IllegalArgumentException e) {
+							// version doesn't parse, assume token is symbolic name without version, or location
+						}
+					}
+				}
+
+				Bundle[] bundles = context.getBundles();
+				for (Bundle b : bundles) {
+					
+					// if symbolicName matches, then matches if there is no version specific on command, or the version matches
+					// if there is no version specified on command, pick first matching bundle
+					if ((symbolicName.equals(b.getSymbolicName()) && (version == null || version.equals(b.getVersion()))) || ((String)in).equals(b.getLocation())) {
+						bundle = b;
+						break;
+					}
+				}
+			}
+			return bundle;
+		}
+		
+		if (desiredType == URL.class) {
+			URL url = null;
+			try {
+				url = new URL((String) in);
+			} catch (Exception e) {
+				//do nothing
+			}
+			return url;
+		}
+		
+		return null;
+	}
+
+	public CharSequence format(Object target, int level, Converter escape) throws Exception {
+		if (target instanceof Dictionary<?, ?>) {
+			Dictionary<?, ?> dic = (Dictionary<?, ?>) target;
+			return printDictionary(dic);
+		}
+		
+		if (target instanceof List<?>) {
+			if (((List<?>)target).get(0) instanceof Dictionary<?, ?>) {
+				StringBuilder builder = new StringBuilder();
+				List<Dictionary<?, ?>> list = (List<Dictionary<?, ?>>) target;
+				for(Dictionary<?, ?> dic : list) {
+					builder.append("Bundle headers:\r\n");
+					builder.append(printDictionary(dic));
+					builder.append("\r\n");
+					builder.append("\r\n");
+				}
+				return builder.toString();
+			}
+		}
+		
+		return null;
+	}
+	
+	private String printDictionary(Dictionary<?, ?> dic) {
+		int count = dic.size();
+		String[] keys = new String[count];
+		Enumeration<?> keysEnum = dic.keys();
+		int i = 0;
+		while (keysEnum.hasMoreElements()) {
+			keys[i++] = (String) keysEnum.nextElement();
+		}
+		Util.sortByString(keys);
+		
+		StringBuilder builder = new StringBuilder();
+		for (i = 0; i < count; i++) {
+			builder.append(" " + keys[i] + " = " + dic.get(keys[i])); //$NON-NLS-1$//$NON-NLS-2$
+			builder.append("\r\n");
+		}
+		builder.append("\r\n");
+		return builder.toString();
+	}
+
+}
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/commands/HelpCommand.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/commands/HelpCommand.java
new file mode 100644
index 0000000..4d3796f
--- /dev/null
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/commands/HelpCommand.java
@@ -0,0 +1,161 @@
+/*******************************************************************************
+ * 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.commands;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Set;
+
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.felix.service.command.Parameter;
+import org.eclipse.equinox.console.command.adapter.CustomCommandInterpreter;
+import org.eclipse.osgi.framework.console.CommandInterpreter;
+import org.eclipse.osgi.framework.console.CommandProvider;
+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;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+/**
+ * This class provides help for the legacy equinox commands, which are adapted to Gogo commands. 
+ */
+public class HelpCommand {
+	private BundleContext context;
+	private Set<CommandProvider> legacyCommandProviders;
+	private ServiceTracker<CommandProvider, Set<CommandProvider>> commandProvidersTracker;
+	private ServiceRegistration<?> registration;
+	
+    public class CommandProviderCustomizer implements ServiceTrackerCustomizer<CommandProvider, Set<CommandProvider>> {
+    	private BundleContext context;
+    	public CommandProviderCustomizer(BundleContext context) {
+    		this.context = context;
+    	}
+    	
+		public Set<CommandProvider> addingService(
+				ServiceReference<CommandProvider> reference) {
+			if (reference.getProperty("osgi.command.function") != null) {
+				// must be a gogo function already; don' track
+				return null;
+			}
+			CommandProvider command = context.getService(reference);
+			legacyCommandProviders.add(command);
+			context.ungetService(reference);
+			return legacyCommandProviders;
+		}
+
+		public void modifiedService(
+				ServiceReference<CommandProvider> reference,
+				Set<CommandProvider> service) {
+			// nothing to do
+		}
+
+		public void removedService(ServiceReference<CommandProvider> reference,
+				Set<CommandProvider> providers) {
+			CommandProvider provider = context.getService(reference);
+			providers.remove(provider);
+		}
+    	
+    }
+	
+	public HelpCommand(BundleContext context) {
+		this.context = context;
+		legacyCommandProviders = new HashSet<CommandProvider>();
+		commandProvidersTracker = new ServiceTracker<CommandProvider, Set<CommandProvider>>(context, CommandProvider.class.getName(), new CommandProviderCustomizer(context));
+		commandProvidersTracker.open();
+	}
+	
+	public void start() {
+		Dictionary<String, Object> props = new Hashtable<String, Object>();
+		props.put(Constants.SERVICE_RANKING, new Integer(Integer.MAX_VALUE));
+		props.put(CommandProcessor.COMMAND_SCOPE, "equinox");
+		props.put(CommandProcessor.COMMAND_FUNCTION, new String[] {"help"});
+		context.registerService(HelpCommand.class.getName(), this, props);
+	}
+	
+	/**
+	 * Provides help for the available commands. The Gogo help command, used with no arguments, prints the names
+	 * of all registered commands. If a command name is passed as argument to the help command, then the help
+	 * message for the particular command is displayed (if such is defined).
+	 * 
+	 * This method can accept an additional argument -legacy. If this option is specified, the names of all 
+	 * legacy equinox commands are displayed, and then the Gogo help command is called to display the other
+	 * commands' names. If -legacy is not specified, then only the Gogo help command is called.
+	 * 
+	 * If -legacy is displayed along with a command name, then the legacy commands are searched
+	 * for a command with this name, and the help message for this command is displayed, if provided. If the 
+	 * CommandProvider, which provides this command, does not provide help for individual commands, then
+	 * the help for all commands in the CommandProvider is displayed. 
+	 * 
+	 * @param session
+	 * @param args
+	 * @throws Exception
+	 */
+	public void help(final CommandSession session, String... args) throws Exception {
+		boolean isLegacy = false;
+		String command = null;
+		
+		if (args.length > 0) {
+			for (String arg : args) {
+				if (arg.equals("-legacy")) {
+					isLegacy = true;
+				} else {
+					command = arg;
+				}
+			}
+		}
+		
+		if (isLegacy == true && command != null) {
+			for (CommandProvider provider : legacyCommandProviders) {
+				Method[] methods = provider.getClass().getMethods();
+				for (Method method : methods) {
+					Object retval = null;
+					if (method.getName().equals("_" + command)) {
+						try {
+							Method helpMethod = provider.getClass().getMethod("_help", CommandInterpreter.class);
+							ArrayList<Object> argsList = new ArrayList<Object>();
+							argsList.add(command);
+							retval = helpMethod.invoke(provider, new CustomCommandInterpreter(argsList));
+						} catch (Exception e) {
+							System.out.println(provider.getHelp());
+							break;
+						}
+						
+						if (retval != null && retval instanceof String) {
+							System.out.println(retval);
+						}
+						break;
+					}
+				}
+			}
+			return;
+		}
+		
+		if (isLegacy == true) {
+			for (CommandProvider provider : legacyCommandProviders) {
+				Method[] methods = provider.getClass().getMethods();
+				for (Method method : methods) {
+					if (method.getName().startsWith("_")) {
+						System.out.println("equinox:" + method.getName().substring(1));
+					}
+				}
+			}
+		} 
+		
+		session.execute("help");
+	}
+}
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/Completer.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/Completer.java
new file mode 100644
index 0000000..48d341a
--- /dev/null
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/Completer.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * 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.common;
+
+import java.util.Map;
+
+/**
+ * This is the interface for providing tab completion.
+ */
+public interface Completer {
+	/**
+	 * Returns the possible candidates for completion for the passed string.
+	 * 
+	 * @param buffer text to be completed
+	 * @param cursor current position in the text
+	 * @return map of candidate completions, and on which position in the buffer starts the completion
+	 */
+	public Map<String, Integer> getCandidates(String buffer, int cursor);
+}
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/ConsoleInputStream.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/ConsoleInputStream.java
index 59dcc50..73ab4ec 100644
--- a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/ConsoleInputStream.java
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/ConsoleInputStream.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2010 SAP AG
+ * Copyright (c) 2010, 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
@@ -18,6 +18,7 @@
  * This class serves as an input stream, which wraps the actual input (e.g. from the telnet) and buffers the lines.
  */
 public class ConsoleInputStream extends InputStream {
+
     private final ArrayList<byte[]> buffer = new ArrayList<byte[]>();
     private byte[] current;
     private int pos;
@@ -54,7 +55,7 @@
 
     }
 
-    public int read(byte b[], int off, int len) throws IOException {
+    /*public int read(byte b[], int off, int len) throws IOException {
         if (len == 0) {
             return len;
         }
@@ -64,6 +65,79 @@
         }
         b[off] = (byte) i;
         return 1;
+    }*/
+    
+    /*public synchronized int read(byte b[], int off, int len) throws IOException {
+        if (len == 0) {
+            return len;
+        }
+        
+        int currOff = off;
+        int readCnt = 0;
+        
+        if (current != null) {
+        	int i;
+        	while (pos > 0 && readCnt < len) {
+        		i = read();
+        		if (i == -1) {
+        			return (readCnt > 0) ? readCnt : i;
+        		}
+        		b[currOff] = (byte) i;
+        		currOff++;
+        		readCnt++;
+        	}
+        } else {
+        	int i = read();
+        	if (i == -1) {
+        		return i;
+        	}
+        	b[currOff] = (byte) i;
+        	currOff++;
+        	readCnt++;
+        	while (pos > 0 && readCnt < len) {
+        		i = read();
+        		if (i == -1) {
+        			return (readCnt > 0) ? readCnt : i;
+        		}
+        		b[currOff] = (byte) i;
+        		currOff++;
+        		readCnt++;
+        	}
+        }
+        
+        return readCnt;
+    }*/
+    
+    public synchronized int read(byte b[], int off, int len) throws IOException {
+        if (len == 0) {
+            return len;
+        }
+        
+        int currOff = off;
+        int readCnt = 0;
+        int i;
+        
+        if (current == null) {
+        	i = read();
+        	if (i == -1) {
+        		return i;
+        	}
+        	b[currOff] = (byte) i;
+        	currOff++;
+        	readCnt++;
+        }
+        
+        while ((pos > 0 || !buffer.isEmpty()) && readCnt < len) {
+    		i = read();
+    		if (i == -1) {
+    			return (readCnt > 0) ? readCnt : i;
+    		}
+    		b[currOff] = (byte) i;
+    		currOff++;
+    		readCnt++;
+    	}
+        
+        return readCnt;
     }
 
     public synchronized void close() throws IOException {
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/ConsoleOutputStream.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/ConsoleOutputStream.java
index 6304bfc..575342c 100644
--- a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/ConsoleOutputStream.java
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/ConsoleOutputStream.java
@@ -92,8 +92,13 @@
      */
     public synchronized void flush() throws IOException {
         if (pos > 0) {
-            out.write(buffer, 0, pos);
-            pos = 0;
+            try {
+				out.write(buffer, 0, pos);
+				out.flush();
+			} finally {
+				pos = 0;
+			}
+            
         }
     }
 
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/InputHandler.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/InputHandler.java
index ef00d67..38451ad 100644
--- a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/InputHandler.java
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/InputHandler.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2010 SAP AG
+ * Copyright (c) 2010, 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
@@ -21,6 +21,7 @@
  * eventually writing to an output stream. This handler should be customized with a concrete content processor.
  */
 public abstract class InputHandler extends Thread {
+
     protected Scanner inputScanner;
     protected OutputStream out;
     protected ConsoleInputStream in;
@@ -45,7 +46,7 @@
             }
         } catch (IOException e) {
             // Printing stack trace is not needed since the streams are closed immediately
-            e.printStackTrace();
+            // do nothing
         } finally {
         	try {
                 in.close();
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/Scanner.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/Scanner.java
index 753a38d..5fb4f08 100644
--- a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/Scanner.java
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/Scanner.java
@@ -14,30 +14,34 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 
-import org.eclipse.equinox.console.telnet.ANSITerminalTypeMappings;
-import org.eclipse.equinox.console.telnet.SCOTerminalTypeMappings;
-import org.eclipse.equinox.console.telnet.TerminalTypeMappings;
-import org.eclipse.equinox.console.telnet.VT100TerminalTypeMappings;
-import org.eclipse.equinox.console.telnet.VT220TerminalTypeMappings;
-import org.eclipse.equinox.console.telnet.VT320TerminalTypeMappings;
+import org.eclipse.equinox.console.common.terminal.ANSITerminalTypeMappings;
+import org.eclipse.equinox.console.common.terminal.SCOTerminalTypeMappings;
+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;
 
 /**
  * A common superclass for content processor for the telnet protocol and for command line editing (processing delete,
  * backspace, arrows, command history, etc.).
  */
 public abstract class Scanner {
-    protected static final byte BS = 8;
-    protected byte BACKSPACE;
+
+	private byte BACKSPACE;
+	private byte DEL;
+    protected static final byte BS = 8;  
     protected static final byte LF = 10;
     protected static final byte CR = 13;
     protected static final byte ESC = 27;
     protected static final byte SPACE = 32;
-    protected byte DEL;
     protected static final byte MAX_CHAR = 127;
     protected static final String DEFAULT_TTYPE = File.separatorChar == '/' ? "XTERM" : "ANSI";
+    // shows if user input should be echoed to the console
+    private boolean isEchoEnabled = true;
 
     protected OutputStream toTelnet;
     protected ConsoleInputStream toShell;
@@ -52,7 +56,7 @@
         supportedEscapeSequences.put("ANSI", new ANSITerminalTypeMappings());
         supportedEscapeSequences.put("VT100", new VT100TerminalTypeMappings());
         VT220TerminalTypeMappings vtMappings = new VT220TerminalTypeMappings();
-        supportedEscapeSequences.put("VT220", new VT220TerminalTypeMappings());
+        supportedEscapeSequences.put("VT220", vtMappings);
         supportedEscapeSequences.put("XTERM", vtMappings);
         supportedEscapeSequences.put("VT320", new VT320TerminalTypeMappings());
         supportedEscapeSequences.put("SCO", new SCOTerminalTypeMappings());
@@ -60,8 +64,14 @@
 
     public abstract void scan(int b) throws IOException;
 
+    public void toggleEchoEnabled(boolean isEnabled) {
+    	isEchoEnabled = isEnabled;
+    }
+    
     protected void echo(int b) throws IOException {
-        toTelnet.write(b);
+    	if (isEchoEnabled) {
+    		toTelnet.write(b);
+    	}
     }
 
     protected void flush() throws IOException {
@@ -116,11 +126,19 @@
 	}
 
 	public String[] getEscapes() {
-		return escapes;
+        if (escapes != null) {
+            return Arrays.copyOf(escapes, escapes.length);
+        } else {
+            return null;
+        }
 	}
 
 	public void setEscapes(String[] escapes) {
-		this.escapes = escapes;
+        if (escapes != null) {
+            this.escapes = Arrays.copyOf(escapes, escapes.length);
+        } else {
+            this.escapes = null;
+        }
 	}
 
 }
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/SimpleByteBuffer.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/SimpleByteBuffer.java
index 0541d55..188b14b 100644
--- a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/SimpleByteBuffer.java
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/SimpleByteBuffer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2010 SAP AG
+ * Copyright (c) 2010, 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
@@ -17,6 +17,7 @@
  * backspace key.
  */
 public class SimpleByteBuffer {
+
     private static int INITAL_SIZE = 13;
 
     private byte[] buffer;
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/ANSITerminalTypeMappings.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/terminal/ANSITerminalTypeMappings.java
similarity index 86%
rename from console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/ANSITerminalTypeMappings.java
rename to console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/terminal/ANSITerminalTypeMappings.java
index 8057abc..0396c27 100644
--- a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/ANSITerminalTypeMappings.java
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/terminal/ANSITerminalTypeMappings.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2010 SAP AG
+ * Copyright (c) 2010, 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
@@ -9,11 +9,13 @@
  *     Lazar Kirchev, SAP AG - initial API and implementation 
  *******************************************************************************/
 
-package org.eclipse.equinox.console.telnet;
+package org.eclipse.equinox.console.common.terminal;
 
 import org.eclipse.equinox.console.common.KEYS;
+import org.eclipse.equinox.console.common.terminal.TerminalTypeMappings;
 
-public class ANSITerminalTypeMappings extends TerminalTypeMappings{
+public class ANSITerminalTypeMappings extends TerminalTypeMappings {
+	
 	public ANSITerminalTypeMappings() {
 		super();
         BACKSPACE = 8;
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/SCOTerminalTypeMappings.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/terminal/SCOTerminalTypeMappings.java
similarity index 90%
rename from console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/SCOTerminalTypeMappings.java
rename to console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/terminal/SCOTerminalTypeMappings.java
index d584987..7bdef7d 100644
--- a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/SCOTerminalTypeMappings.java
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/terminal/SCOTerminalTypeMappings.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2010 SAP AG
+ * Copyright (c) 2010, 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
@@ -9,11 +9,12 @@
  *     Lazar Kirchev, SAP AG - initial API and implementation  
  *******************************************************************************/
 
-package org.eclipse.equinox.console.telnet;
+package org.eclipse.equinox.console.common.terminal;
 
 import org.eclipse.equinox.console.common.KEYS;
 
 public class SCOTerminalTypeMappings extends TerminalTypeMappings {
+	
 	public SCOTerminalTypeMappings() {
 		super();
 		
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/TerminalTypeMappings.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/terminal/TerminalTypeMappings.java
similarity index 87%
rename from console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/TerminalTypeMappings.java
rename to console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/terminal/TerminalTypeMappings.java
index a1d95b2..166393a 100644
--- a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/TerminalTypeMappings.java
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/terminal/TerminalTypeMappings.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2010 SAP AG
+ * Copyright (c) 2010, 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
@@ -9,8 +9,9 @@
  *     Lazar Kirchev, SAP AG - initial API and implementation  
  *******************************************************************************/
 
-package org.eclipse.equinox.console.telnet;
+package org.eclipse.equinox.console.common.terminal;
 
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -42,7 +43,11 @@
 	}
 	
 	public String[] getEscapes() {
-		return escapes;
+        if (escapes != null) {
+            return Arrays.copyOf(escapes, escapes.length);
+        } else {
+            return null;
+        }
 	}
 	
 	public byte getBackspace() {
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/VT100TerminalTypeMappings.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/terminal/VT100TerminalTypeMappings.java
similarity index 92%
rename from console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/VT100TerminalTypeMappings.java
rename to console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/terminal/VT100TerminalTypeMappings.java
index d65d1d0..19d36d4 100644
--- a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/VT100TerminalTypeMappings.java
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/terminal/VT100TerminalTypeMappings.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2010 SAP AG
+ * Copyright (c) 2010, 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
@@ -9,11 +9,12 @@
  *     Lazar Kirchev, SAP AG - initial API and implementation 
  *******************************************************************************/
 
-package org.eclipse.equinox.console.telnet;
+package org.eclipse.equinox.console.common.terminal;
 
 import org.eclipse.equinox.console.common.KEYS;
 
 public class VT100TerminalTypeMappings extends TerminalTypeMappings {
+	
 	public VT100TerminalTypeMappings() {
 		super();
         BACKSPACE = 127;
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/VT220TerminalTypeMappings.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/terminal/VT220TerminalTypeMappings.java
similarity index 89%
rename from console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/VT220TerminalTypeMappings.java
rename to console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/terminal/VT220TerminalTypeMappings.java
index 6b5b90f..9b832d9 100644
--- a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/VT220TerminalTypeMappings.java
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/terminal/VT220TerminalTypeMappings.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2010 SAP AG
+ * Copyright (c) 2010, 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
@@ -9,7 +9,7 @@
  *     Lazar Kirchev, SAP AG - initial API and implementation 
  *******************************************************************************/
 
-package org.eclipse.equinox.console.telnet;
+package org.eclipse.equinox.console.common.terminal;
 
 /**
  * For the supported escape sequences, the VT220 and XTERM sequences
@@ -17,6 +17,7 @@
  *
  */
 public class VT220TerminalTypeMappings extends ANSITerminalTypeMappings {
+	
 	public VT220TerminalTypeMappings() {
 		super();
 		
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/VT320TerminalTypeMappings.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/terminal/VT320TerminalTypeMappings.java
similarity index 92%
rename from console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/VT320TerminalTypeMappings.java
rename to console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/terminal/VT320TerminalTypeMappings.java
index 7719be8..5fcf382 100644
--- a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/VT320TerminalTypeMappings.java
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/common/terminal/VT320TerminalTypeMappings.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2010 SAP AG
+ * Copyright (c) 2010, 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
@@ -9,11 +9,12 @@
  *     Lazar Kirchev, SAP AG - initial API and implementation  
  *******************************************************************************/
 
-package org.eclipse.equinox.console.telnet;
+package org.eclipse.equinox.console.common.terminal;
 
 import org.eclipse.equinox.console.common.KEYS;
 
 public class VT320TerminalTypeMappings extends TerminalTypeMappings {
+	
 	public VT320TerminalTypeMappings() {
 		super();
         BACKSPACE = 8;
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/completion/CommandLineParser.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/completion/CommandLineParser.java
new file mode 100644
index 0000000..ecb18fb
--- /dev/null
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/completion/CommandLineParser.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * 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.completion;
+
+/**
+ *  This class determines the last token in the command line. Completion should be made for this token. 
+ */
+public class CommandLineParser {
+	private static final char[] delimiters = new char[] {'|', ';', '=', '{', '}', '(', ')', '$', '>', ']', ' '};
+	private static final String COMMENT_CHAR = "#";
+	private static final String HASH_MAP_DEF_START_CHAR = "<";
+	private static final String HASH_MAP_DEF_END_CHAR = ">";
+	private static final String LIST_DEF_START_CHAR = "[";
+	/**
+	 * Determine the last token in the command line. The last token is the substring, starting from 
+	 * one of the characters, considered as delimiters
+	 * 
+	 * @param commandLine whole command line
+	 * @param cursor current position in the command line
+	 * @return the current token
+	 */
+	public static String getCurrentToken(String commandLine, int cursor) {
+		String current = commandLine.substring(0, cursor);
+	
+		int currentStartIdx = -1;
+		// determine the positioin of the last delimiter
+		for(char delimiter : delimiters) {
+			int idx = current.lastIndexOf(delimiter);
+			if (delimiter == '=' && idx > -1) {
+				// hash map is defined in a command with the syntax <key=value>; within this definition we do not want to 
+				// make completion; determine if we are in such case
+				int startAngleBraceIdx = current.substring(0, idx).lastIndexOf(HASH_MAP_DEF_START_CHAR);
+				int endAngleBraceBeforeAssignmentIdx = current.substring(0, idx).lastIndexOf(HASH_MAP_DEF_END_CHAR);
+				int endAngleBraceAfterAssignmentIdx = current.substring(idx + 1).indexOf(HASH_MAP_DEF_END_CHAR);
+				if (startAngleBraceIdx > -1 && startAngleBraceIdx < idx && endAngleBraceBeforeAssignmentIdx == -1 && endAngleBraceAfterAssignmentIdx == -1) {
+					return null;
+				}
+			}
+			if (idx > currentStartIdx) {
+				currentStartIdx = idx;
+			}
+		}
+		
+		if (currentStartIdx + 1 == current.length()) {
+			return "";
+		}
+		
+		if (currentStartIdx + 1 > current.length()) {
+			return null;
+		}
+		
+		String currentToken = current.substring(currentStartIdx + 1, current.length());
+		
+		// if the current position is after the comment character, or within a hash map or list definition, do not do command completion
+		if (currentToken.contains(COMMENT_CHAR) || currentToken.contains(HASH_MAP_DEF_START_CHAR) || currentToken.contains(LIST_DEF_START_CHAR)) {
+			return null;
+		}
+		
+		return currentToken;
+	}
+}
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/completion/CommandNamesCompleter.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/completion/CommandNamesCompleter.java
new file mode 100644
index 0000000..a5715d9
--- /dev/null
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/completion/CommandNamesCompleter.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * 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.completion;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.service.command.CommandSession;
+import org.eclipse.equinox.console.common.Completer;
+
+/**
+ * This class provides completion for command names. 
+ *
+ */
+public class CommandNamesCompleter implements Completer {
+
+	private CommandSession session;
+	private static final String COMMANDS = ".commands";
+	
+	public CommandNamesCompleter(CommandSession session) {
+		this.session = session;
+	}
+	
+	public Map<String, Integer> getCandidates(String buffer, int cursor) {
+		// CommandSession.get(".commands") returns the names of all registered commands
+		Set<String> commandNames = (Set<String>) session.get(COMMANDS);
+		
+		// command names are stored in the session in lower case
+		String currentToken = CommandLineParser.getCurrentToken(buffer, cursor).toLowerCase();
+		if(currentToken == null || currentToken.equals("")) {
+			return new HashMap<String, Integer>();
+		}
+		
+		if (!currentToken.contains(":")) {
+			// the current token does not contain a scope qualifier, so remove scopes from possible candidates
+			commandNames = clearScopes(commandNames);
+		} 
+		StringsCompleter completer = new StringsCompleter(commandNames, true);
+		return completer.getCandidates(buffer, cursor);
+	}
+	
+	private Set<String> clearScopes(Set<String> commandNames) {
+		Set<String> clearedCommandNames = new HashSet<String>();
+		
+		for(String commandName : commandNames) {
+			clearedCommandNames.add(commandName.substring(commandName.indexOf(":") + 1));
+		}
+		
+		return clearedCommandNames;
+	}
+}
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/completion/CompletionHandler.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/completion/CompletionHandler.java
new file mode 100644
index 0000000..60ed1a5
--- /dev/null
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/completion/CompletionHandler.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * 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.completion;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.apache.felix.service.command.CommandSession;
+import org.eclipse.equinox.console.common.Completer;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * This class aggregates the different types of completers - variable, command and
+ * file completers. It also searches for registered custom completers and if available
+ * uses them too. It call all completers and finally returns the completion candidates
+ * returned from all of them.
+ *
+ */
+public class CompletionHandler {
+	
+	private	BundleContext context;
+	private CommandSession session;
+	Set<Completer> completers;
+	private static final String FILE = "file";
+	private static final char VARIABLE_PREFIX = '$';
+	
+	public CompletionHandler(BundleContext context, CommandSession session) {
+		this.context = context;
+		this.session = session;
+		completers = new HashSet<Completer>();
+	}
+	
+	public Map<String, Integer> getCandidates(byte[] buf, int cursor) {
+        String currentInput = new String(buf);
+        String currentToken = CommandLineParser.getCurrentToken(currentInput, cursor);
+        if (currentToken ==  null){
+        	return new HashMap<String, Integer>();
+        }
+        if (currentToken.contains(FILE) == true) {
+        	completers.add(new FileNamesCompleter());
+        }else{
+         	if ((cursor - currentToken.length() > 0) && (buf[cursor - currentToken.length() - 1] == VARIABLE_PREFIX)){
+        		completers.add(new VariableNamesCompleter(session));
+        	}else {
+        		completers.add(new CommandNamesCompleter(session));
+        		completers.add(new FileNamesCompleter());
+        	}
+        }
+        lookupCustomCompleters();
+		Map<String, Integer> candidates = new TreeMap<String, Integer>();
+		for (Completer completer : completers) {
+			candidates.putAll(completer.getCandidates(currentInput, cursor));
+		}
+		
+		return candidates;
+	}
+	
+	private void lookupCustomCompleters (){
+		ServiceReference<Completer>[] completersRefs = null;
+		try {
+			completersRefs = (ServiceReference<Completer>[]) context.getServiceReferences(Completer.class.getName(), null);
+		} catch (InvalidSyntaxException e) {
+			// do nothing
+		}
+		
+		if (completersRefs != null) {
+			for (ServiceReference<Completer> ref : completersRefs) {
+				Completer completer = context.getService(ref);
+				if (completer != null) {
+					completers.add(completer);
+				}
+			}
+		}
+	}
+
+}
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/completion/FileNamesCompleter.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/completion/FileNamesCompleter.java
new file mode 100644
index 0000000..ba14bca
--- /dev/null
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/completion/FileNamesCompleter.java
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * 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.completion;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.equinox.console.common.Completer;
+
+/**
+ * This class implements completion of file names. It provides completion both for 
+ * files with absolute filenames, as well as with names, relative to the current
+ * directory.
+ */
+public class FileNamesCompleter implements Completer {
+	private static final String FILE = "file:";
+	
+	public Map<String, Integer> getCandidates(String buffer, int cursor) {
+		Map<String, Integer> result = new HashMap<String, Integer>();
+		String currentToken = CommandLineParser.getCurrentToken(buffer, cursor);
+		if(currentToken == null || currentToken.equals("")) {
+			return new HashMap<String, Integer>();
+		}
+		
+		// if current token contains file:, then use URL class to parse the filename
+		if(currentToken.contains(FILE)) {
+			String fileName = currentToken.substring(currentToken.indexOf(FILE));
+			try {
+				URL url = new URL(fileName);
+				String canonicalFileName = url.getPath();
+				File file = new File(canonicalFileName);
+				File parent = file.getParentFile();
+				 
+				if ((file.isDirectory() && canonicalFileName.endsWith("/") )|| parent == null) {
+					// the entered filename is a directory name, ending with file separator character - here 
+					// all files in the directory will be returned as completion candidates; 
+					// or, if parent is null, the file is in the root directory and 
+					// the names of all files in this directory should be used to search for completion candidates
+					return checkChildren(file, "", cursor, false);
+				} else {
+					// there is a filename for completion, and the names of all files in the same directory will be used
+					// to search for completion candidates
+					return checkChildren(parent, file.getName(), cursor, false);
+				}
+			} catch (MalformedURLException e) {
+				return result;
+			}
+		}
+		
+		// the file name for completion is only the file separator character, so all files 
+		// in the current directory will be returned as completion candidates
+		if (currentToken.equals("\\\\") || currentToken.equals("/")) {
+			File file = new File(".");
+			return checkChildren(file, "", cursor, false);
+		}
+		
+		// if the current token contains file separator character, then its parent directory can be extracted
+		if (currentToken.contains("\\\\") || currentToken.contains("/")) {
+			File file = new File(currentToken);
+			File parent = file.getParentFile();
+			if ((file.isDirectory() && (currentToken.endsWith("/") || currentToken.endsWith("\\\\")))
+					|| parent == null) {
+				// the entered filename is a directory name, ending with file separator character - here 
+				// all files in the directory will be returned as completion candidates; 
+				// or, if parent is null, the file is in the root directory and 
+				// the names of all files in this directory should be used to search for completion candidates
+				return checkChildren(file, "", cursor, false);
+			} else {
+				// there is a filename for completion, and the names of all files in the same directory will be used
+				// to search for completion candidates
+				return checkChildren(parent, file.getName(), cursor, false);
+			}
+		}
+		
+		// if the current token does not contain file separator character, 
+		// then search for candidates in the current directory
+		return checkChildren(new File("."), currentToken, cursor, false);
+	}
+
+	private Map<String, Integer> checkChildren(File parent, String nameToComplete, int cursor, boolean absolute) {
+		Map<String, Integer> result = new HashMap<String, Integer>();
+		if(parent.exists()) {
+			File[] children = parent.listFiles();
+			for(File child : children) {
+				if(child.getName().startsWith(nameToComplete)) {
+					if(absolute == true) {
+						result.put(child.getAbsolutePath(), cursor - nameToComplete.length());
+					} else {
+						result.put(child.getName(), cursor - nameToComplete.length());
+					}
+				}
+			}
+		}
+		return result;
+	}
+}
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/completion/StringsCompleter.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/completion/StringsCompleter.java
new file mode 100644
index 0000000..e64af3c
--- /dev/null
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/completion/StringsCompleter.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * 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.completion;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.equinox.console.common.Completer;
+
+/**
+ * This class provides completion for arbitrary strings
+ *
+ */
+public class StringsCompleter implements Completer {
+
+	private Set<String> strings;
+	private boolean isCaseSensitive;
+	
+	public StringsCompleter(Set<String> strings, boolean isCaseSensitive) {
+		this.strings = strings;
+		this.isCaseSensitive = isCaseSensitive;
+	}
+	
+	public Map<String, Integer> getCandidates(String buffer, int cursor) {
+		String currentToken = CommandLineParser.getCurrentToken(buffer, cursor);
+		if (currentToken == null) {
+			return new HashMap<String, Integer>();
+		}
+		if (!isCaseSensitive) {
+			currentToken = currentToken.toLowerCase();
+		}
+		
+		int startIndex = cursor - currentToken.length();
+		
+		// if currentToken is empty string, then there is nothing to complete
+		// the only exception is if the previous character is $, which signifies
+		// that a variable name is expected; in this case all strings will be 
+		// returned as candidates
+		if(currentToken.equals("") && buffer.charAt(startIndex - 1) != '$') {
+			return new HashMap<String, Integer>();
+		}
+		
+		Map<String, Integer> result = new HashMap<String, Integer>();
+
+		for(String candidate : strings) {
+			if (isCaseSensitive) {
+				if (candidate.startsWith(currentToken)) {
+					result.put(candidate, startIndex);
+				}
+			} else {
+				if (candidate.toLowerCase().startsWith(currentToken)) {
+					result.put(candidate, startIndex);
+				}
+			}
+		}
+		
+		return result;
+	}
+
+}
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/completion/VariableNamesCompleter.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/completion/VariableNamesCompleter.java
new file mode 100644
index 0000000..5324f53
--- /dev/null
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/completion/VariableNamesCompleter.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * 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.completion;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.service.command.CommandSession;
+import org.eclipse.equinox.console.common.Completer;
+
+/**
+ * This class provides completion for gogo session variables. 
+ *
+ */
+public class VariableNamesCompleter implements Completer {
+
+	private CommandSession session; 
+	
+	public VariableNamesCompleter(CommandSession session) {
+		this.session = session;
+	}
+	
+	public Map<String, Integer> getCandidates(String buffer, int cursor) {
+		// CommandSession.get(null) returns the names of all registered varialbes
+		Set<String> variableNames = (Set<String>) session.get(null);
+		StringsCompleter completer = new StringsCompleter(variableNames, false);
+		return completer.getCandidates(buffer, cursor);
+	}
+
+}
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/jaas/RolePrincipal.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/jaas/RolePrincipal.java
new file mode 100644
index 0000000..c5f8c4a
--- /dev/null
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/jaas/RolePrincipal.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * 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.jaas;
+
+import java.security.Principal;
+
+/**
+ * This class represents a user role
+ *
+ */
+public class RolePrincipal implements Principal {
+	private String roleName;
+	
+	public RolePrincipal(String roleName) {
+		this.roleName = roleName;
+	}
+
+	public String getName() {
+		return roleName;
+	}
+	
+	public boolean equals(Object role) {
+		
+		if (role == null) {
+			return false;
+		}
+		
+		if (this == role) {
+			return true;
+		}
+		
+		if (!(role instanceof RolePrincipal)) {
+			return false;
+		}
+		
+		RolePrincipal otherRole = (RolePrincipal) role;
+		if (roleName != null) {
+			if (roleName.equals(otherRole.roleName)) {
+				return true;
+			} else {
+				return false;
+			}
+		} else {
+			if (otherRole.roleName == null) {
+				return true;
+			} else {
+				return false;
+			}
+		}
+	}
+
+	@Override
+	public int hashCode() {
+		int result = 1;
+		result = 73 * result + (roleName == null ? 0 : roleName.hashCode());
+		return result;
+	}
+}
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/jaas/SecureStorageLoginModule.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/jaas/SecureStorageLoginModule.java
new file mode 100644
index 0000000..941fb17
--- /dev/null
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/jaas/SecureStorageLoginModule.java
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * 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.jaas;
+
+import java.io.IOException;
+import java.util.Map;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.FailedLoginException;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.spi.LoginModule;
+
+import org.eclipse.equinox.console.storage.DigestUtil;
+import org.eclipse.equinox.console.storage.SecureUserStore;
+
+/**
+ * This class implements a JAAS LoginModule, which performs username/password
+ * based authentication. It reads the user data from the store. 
+ *
+ */
+public class SecureStorageLoginModule implements LoginModule {
+	
+	private volatile Subject subject;
+	private volatile CallbackHandler callbackHandler;
+	private volatile UserPrincipal userPrincipal;
+	private volatile boolean isSuccess;
+
+	public void initialize(Subject subject, CallbackHandler callbackHandler,
+			Map<String, ?> sharedState, Map<String, ?> options) {
+		this.subject = subject;
+		this.callbackHandler = callbackHandler;
+	}
+
+	public boolean login() throws LoginException {
+		NameCallback nameCallback = new NameCallback("username: ");
+		PasswordCallback passwordCallback = new PasswordCallback("password: ", false);
+		try {
+			callbackHandler.handle(new Callback[]{nameCallback, passwordCallback});
+		} catch (IOException e) {
+			throw new FailedLoginException("Cannot get username and password");
+		} catch (UnsupportedCallbackException e) {
+			throw new FailedLoginException("Cannot get username and password");
+		}
+		
+		String username = nameCallback.getName();
+		char[] password = passwordCallback.getPassword();
+		
+		userPrincipal = getUserInfo(username);
+		
+		try {
+			isSuccess = userPrincipal.authenticate(DigestUtil.encrypt(new String(password)).toCharArray());
+		} catch (Exception e) {
+			throw new FailedLoginException("Wrong credentials");
+		}
+		
+		if (isSuccess == true) {
+			return isSuccess;
+		} else {
+			throw new FailedLoginException("Wrong credentials");
+		}
+	}
+
+	public boolean commit() throws LoginException {
+		if (isSuccess == true) {
+			synchronized (this) {
+				subject.getPrincipals().add(userPrincipal);
+				subject.getPrincipals().addAll(userPrincipal.getRoles());
+			}
+			return true;
+		} else {
+			userPrincipal.destroy();
+			userPrincipal = null;
+			return false;
+		}
+	}
+
+	public boolean abort() throws LoginException {
+		userPrincipal.destroy();
+		userPrincipal = null;
+		return true;
+	}
+
+	public boolean logout() throws LoginException {
+		synchronized (this) {
+			subject.getPrincipals().remove(userPrincipal);
+			subject.getPrincipals().removeAll(userPrincipal.getRoles());
+		}
+		subject = null;
+		userPrincipal.destroy();
+		userPrincipal = null;
+		return true;
+	}
+	
+	private UserPrincipal getUserInfo(String username) throws FailedLoginException {
+		try {
+			if (!SecureUserStore.existsUser(username)) {
+				throw new FailedLoginException("Wrong credentials");
+			}
+			
+			String password = SecureUserStore.getPassword(username);
+			if (password == null) {
+				throw new FailedLoginException("Corrupted user");
+			}
+			
+			String roles = SecureUserStore.getRoles(username);
+			if (roles == null) {
+				roles = "";
+			}
+			
+			UserPrincipal userPrincipal = new UserPrincipal(username, password);
+			for (String role : roles.split(",")) {
+				userPrincipal.addRole(new RolePrincipal(role));
+			}
+			
+			return userPrincipal;
+		} catch (Exception e) {
+			throw new FailedLoginException(e.getMessage());
+		}
+	}
+	
+}
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/jaas/UserPrincipal.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/jaas/UserPrincipal.java
new file mode 100644
index 0000000..7af4162
--- /dev/null
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/jaas/UserPrincipal.java
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * 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.jaas;
+
+import java.security.Principal;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * This class represents a user with password and roles
+ *
+ */
+public class UserPrincipal implements Principal {
+	private String username;
+	private char[] password;
+	private Set<RolePrincipal> rolePrincipals = new HashSet<RolePrincipal>();
+	
+	public UserPrincipal(String username, String password) {
+		this.username = username;
+		this.password = new char[password.length()];
+		System.arraycopy(password.toCharArray(), 0, this.password, 0, this.password.length);
+	}
+	
+	public String getName() {
+		return username;
+	}
+	
+	public boolean authenticate(char[] password) {
+		if (password == null) {
+			return false;
+		}
+		
+		if (this.password == null) {
+			return false;
+		}
+		
+		if (this.password.length != password.length) {
+			return false;
+		}
+		
+		for(int i = 0; i < this.password.length; i++) {
+			if(this.password[i] != password[i]) {
+				return false;
+			}
+		}
+		
+		return true;
+	}
+	
+	public Set<RolePrincipal> getRoles() {
+		return rolePrincipals;
+	}
+	
+	public synchronized void addRole(RolePrincipal rolePrincipal) {
+		rolePrincipals.add(rolePrincipal);
+	}
+	
+	public boolean equals(Object userPrincipal) {
+		if (userPrincipal == null) {
+			return false;
+		}
+		
+		if (this == userPrincipal) {
+			return true;
+		}
+		
+		if (! (userPrincipal instanceof UserPrincipal)) {
+			return false;
+		}
+		
+		UserPrincipal otherUser = (UserPrincipal) userPrincipal;
+		if (username != null) {
+			if (!username.equals(otherUser.username)) {
+				return false;
+			}
+		} else {
+			if (otherUser.username != null) {
+				return false;
+			}
+		}
+		
+		if (password != null) {
+			if (otherUser.password == null) {
+				return false;
+			}
+			
+			if (password.length != otherUser.password.length) {
+				return false;
+			}
+			
+			for(int i = 0; i < password.length; i++) {
+				if (password[i] != otherUser.password[i]) {
+					return false;
+				}
+			}
+		} else {
+			if (otherUser.username != null) {
+				return false;
+			}
+		}
+		
+		if (rolePrincipals != null) {
+			if (!(rolePrincipals.equals(otherUser.rolePrincipals))) {
+				return false;
+			}
+		} else {
+			if (otherUser.rolePrincipals != null) {
+				return false;
+			}
+		}
+		
+		return true;
+	}
+	
+	public void destroy() {
+		for(int i = 0; i < password.length; i++) {
+			password[i] = ' ';
+		}
+		
+		password = null;
+	}
+
+	@Override
+	public int hashCode() {
+		int result = 1;
+		result = 73 * result + (username == null ? 0 : username.hashCode());
+		result = 73 * result + (password == null ? 0 : new String(password).hashCode());
+		result = 73 * result + (rolePrincipals == null ? 0 : rolePrincipals.hashCode());
+		return result;
+	}
+}
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
new file mode 100644
index 0000000..466d25a
--- /dev/null
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/SshCommand.java
@@ -0,0 +1,230 @@
+/*******************************************************************************
+ * 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.IOException;
+import java.net.BindException;
+import java.net.ServerSocket;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.Descriptor;
+import org.eclipse.equinox.console.storage.DigestUtil;
+import org.eclipse.equinox.console.storage.SecureUserStore;
+import org.osgi.framework.BundleContext;
+
+/**
+ * This class implements a command for starting/stopping a simple ssh server.
+ *
+ */
+public class SshCommand {
+	private String defaultHost = null;
+	private int defaultPort;
+    private final CommandProcessor processor;
+    private String host = null;
+    private int port;
+    private SshServ sshServ;
+    private BundleContext context;
+    
+    private static final String DEFAULT_USER = "equinox";
+    private static final String DEFAULT_PASSWORD = "equinox";
+    private static final String DEFAULT_USER_STORE_PROPERTY = "osgi.console.ssh.useDefaultSecureStorage";
+    
+    public SshCommand(CommandProcessor processor, BundleContext context) {
+        this.processor = processor;
+        this.context = context;
+        
+        String sshPort = null;
+        String consolePropValue = context.getProperty("osgi.console.ssh");
+        if(consolePropValue != null) {
+        	int index = consolePropValue.lastIndexOf(":");
+        	if (index > -1) {
+        		defaultHost = consolePropValue.substring(0, index);
+        	}
+        	sshPort = consolePropValue.substring(index + 1);
+        }
+        if (sshPort != null && !"".equals(sshPort)) {
+        	try {
+        		defaultPort = Integer.parseInt(sshPort);
+			} catch (NumberFormatException e) {
+				// do nothing
+			}
+        } 
+    }
+    
+    public synchronized void start() {
+    	Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put("osgi.command.scope", "equinox");
+		properties.put("osgi.command.function", new String[] {"ssh"});
+		if (port > 0 || defaultPort > 0) {
+			try{
+				ssh(new String[]{"start"});
+			} catch (Exception e) {
+				System.out.println("Cannot start ssh. Reason: " + e.getMessage());
+				e.printStackTrace();
+			}
+		}
+		context.registerService(SshCommand.class.getName(), this, properties);
+    }
+
+    @Descriptor("start/stop a ssh server")
+    public synchronized void ssh(String[] arguments) throws Exception {
+        String command = null;
+        String newHost = null;
+        int newPort = 0;
+        
+        for(int i = 0; i < arguments.length; i++) {
+        	if("-?".equals(arguments[i]) || "-help".equals(arguments[i])) {
+        		printHelp();
+        		return;
+        	} else if("start".equals(arguments[i])) {
+        		command = "start";
+        	} else if ("stop".equals(arguments[i])) {
+        		command = "stop";
+        	} else if ("-port".equals(arguments[i]) && (arguments.length > i + 1)) {
+        		i++;
+        		newPort = Integer.parseInt(arguments[i]);
+        	} else if ("-host".equals(arguments[i]) && (arguments.length > i + 1)) {
+        		i++;
+        		newHost = arguments[i];
+        	} else {
+        		throw new Exception("Unrecognized ssh command/option " + arguments[i]);
+        	}
+        }
+        
+        if (command == null) {
+        	throw new Exception("No ssh command specified");
+        }
+        
+        if (newPort != 0) {
+        	port = newPort;
+        } else if (port == 0) {
+        	port = defaultPort;
+        }
+        
+        if (port == 0) {
+        	throw new Exception("No ssh port specified");
+        }
+        
+        if (newHost != null) {
+        	host = newHost;
+        } else {
+        	host = defaultHost;
+        }
+        
+        if ("start".equals(command)) {
+            if (sshServ != null) {
+                throw new IllegalStateException("ssh is already running on port " + port);
+            }
+            
+            checkPortAvailable(port);
+            
+            try {
+				sshServ = new SshServ(processor, 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");
+				sshServ = null;
+				return;
+			}
+            sshServ.setName("equinox ssh");
+            
+            if ("true".equals(context.getProperty(DEFAULT_USER_STORE_PROPERTY))) {
+            	try {
+            		checkUserStore();
+            		registerUserAdmin();
+            	} catch (NoClassDefFoundError e) {
+            		System.out.println("If you want to use secure storage, please install Equinox security bundle and its dependencies");
+            		sshServ = null;
+    				return;
+            	} catch (IOException e) {
+            		e.printStackTrace();
+            		sshServ = null;
+    				return;
+            	}
+            }
+            
+            try {
+				sshServ.start();
+			} catch (RuntimeException e) {
+				sshServ = null;
+				return;
+			}    
+        } else if ("stop".equals(command)) {
+            if (sshServ == null) {
+                throw new IllegalStateException("ssh is not running.");
+            }
+            
+            sshServ.stopSshServer();
+            sshServ = null;
+        } 
+    }
+    
+    private void checkPortAvailable(int port) throws Exception {
+    	ServerSocket socket = null;
+    	try {
+    		socket = new ServerSocket(port);
+    	} catch (BindException e) {
+    		throw new Exception ("Port " + port + " already in use");
+    	} finally {
+    		if (socket != null) {
+    			socket.close();
+    		}
+    	}
+    }
+    
+    /*
+     * Register user administration commands
+     */
+    private void registerUserAdmin() {
+    	Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put("osgi.command.scope", "equinox");
+		properties.put("osgi.command.function", new String[] {"addUser", "addUser", "deleteUser", "resetPassword", "setPassword", "addRoles", "removeRoles", "listUsers"});
+		context.registerService(UserAdminCommand.class.getName(), new UserAdminCommand(), properties);
+    }
+    
+    /*
+     * Create user store if not available. Add the default user, if there is no other user in the store.
+     */
+    private void checkUserStore() throws Exception {
+    	SecureUserStore.initStorage();
+    	if(SecureUserStore.getUserNames().length == 0) {
+    		SecureUserStore.putUser(DEFAULT_USER, DigestUtil.encrypt(DEFAULT_PASSWORD), null );
+    	}
+    }
+    
+    private void printHelp() {
+    	StringBuffer help = new StringBuffer();
+    	help.append("ssh - start simple ssh server");
+    	help.append("\n");
+    	help.append("Usage: ssh start | stop [-port port] [-host host]");
+    	help.append("\n");
+    	help.append("\t");
+    	help.append("-port");
+    	help.append("\t");
+    	help.append("listen port (default=");
+    	help.append(defaultPort);
+    	help.append(")");
+    	help.append("\n");
+    	help.append("\t");
+    	help.append("-host");
+    	help.append("\t");
+    	help.append("local host address to listen on (default is none - listen on all network interfaces)");
+    	help.append("\n");
+    	help.append("\t");
+    	help.append("-?, -help");
+    	help.append("\t");
+    	help.append("show help");
+    	System.out.println(help.toString());          
+    }
+}
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/SshInputHandler.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/SshInputHandler.java
new file mode 100644
index 0000000..d1b2eb7
--- /dev/null
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/SshInputHandler.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * 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.InputStream;
+
+import org.eclipse.equinox.console.common.ConsoleInputStream;
+import org.eclipse.equinox.console.common.ConsoleOutputStream;
+import org.eclipse.equinox.console.common.InputHandler;
+
+/**
+ * This class customizes the generic handler with a concrete content processor,
+ * which provides ssh protocol handling.
+ *
+ */
+public class SshInputHandler extends InputHandler {
+	public SshInputHandler(InputStream input, ConsoleInputStream in, ConsoleOutputStream out) {
+        super(input, in, out);
+        inputScanner = new SshInputScanner(in, out);
+    }
+}
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/SshInputScanner.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/SshInputScanner.java
new file mode 100644
index 0000000..f0c88a9
--- /dev/null
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/SshInputScanner.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * 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.IOException;
+import java.io.OutputStream;
+
+import org.eclipse.equinox.console.common.ConsoleInputStream;
+import org.eclipse.equinox.console.common.KEYS;
+import org.eclipse.equinox.console.common.Scanner;
+import org.eclipse.equinox.console.common.terminal.TerminalTypeMappings;
+
+/**
+ * This class performs preprocessing of the input from the ssh server in order to echo the visible
+ * characters back to the console.
+ *
+ */
+public class SshInputScanner extends Scanner {
+
+	public SshInputScanner(ConsoleInputStream toShell, OutputStream toTelnet) {
+		super(toShell, toTelnet);
+        TerminalTypeMappings currentMapping = supportedEscapeSequences.get(DEFAULT_TTYPE);
+    	currentEscapesToKey = currentMapping.getEscapesToKey();
+    	escapes = currentMapping.getEscapes();
+    	setBackspace(currentMapping.getBackspace());
+    	setDel(currentMapping.getDel());
+	}
+	
+	@Override
+	public void scan(int b) throws IOException {
+		b &= 0xFF;
+
+		if (isEsc) {
+            scanEsc(b);
+        } else {
+        	switch (b) {
+        	case ESC:
+        		startEsc();
+        		toShell.add(new byte[]{(byte) b});
+        		break;
+        	default:
+        		if (b >= SPACE && b < MAX_CHAR) {
+        			echo((byte) b);
+        			flush();
+        		}
+        		toShell.add(new byte[]{(byte) b});
+        	}
+        }
+	}
+
+	@Override
+	protected void scanEsc(int b) throws IOException {
+		esc += (char) b;
+        toShell.add(new byte[]{(byte) b});
+        KEYS key = checkEscape(esc);
+        if (key == KEYS.UNFINISHED) {
+            return;
+        }
+        if (key == KEYS.UNKNOWN) {
+            isEsc = false;
+            scan(b);
+            return;
+        }
+        isEsc = false;
+	}
+
+}
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
new file mode 100644
index 0000000..1f10179
--- /dev/null
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/SshServ.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * 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.IOException;
+
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.sshd.SshServer;
+import org.apache.sshd.server.PasswordAuthenticator;
+import org.apache.sshd.server.jaas.JaasPasswordAuthenticator;
+import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
+import org.osgi.framework.BundleContext;
+
+/**
+ *  This class configures and start an ssh server
+ *
+ */
+public class SshServ extends Thread {
+	private int port;
+	private String host;
+	private SshServer sshServer = null;
+	private SshShellFactory shellFactory = null;
+	
+	private static final String SSH_KEYSTORE_PROP = "ssh.server.keystore";
+	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) {
+    	this.host = host;
+    	this.port = port;
+    	shellFactory = new SshShellFactory(processor, context);
+    }
+    
+    public void run() throws RuntimeException {
+    	try {
+			sshServer = SshServer.setUpDefaultServer();
+		} catch (NoClassDefFoundError e1) {
+			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");
+			throw new RuntimeException("SSH bundles not available");
+		}
+		if (host != null) {
+			sshServer.setHost(host);
+		}
+    	sshServer.setPort(port);
+    	sshServer.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(System.getProperty(SSH_KEYSTORE_PROP, SSH_KEYSTORE_PROP_DEFAULT)));
+    	sshServer.setShellFactory(shellFactory);
+    	sshServer.setPasswordAuthenticator(createJaasPasswordAuthenticator());
+    	try {
+			sshServer.start();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+    }
+    
+    public synchronized void stopSshServer() {
+    	try {
+			sshServer.stop(true);
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+		}
+    }
+    
+    private PasswordAuthenticator createJaasPasswordAuthenticator() {
+            JaasPasswordAuthenticator jaasPasswordAuthenticator = new JaasPasswordAuthenticator();
+            jaasPasswordAuthenticator.setDomain(EQUINOX_CONSOLE_DOMAIN);
+            return jaasPasswordAuthenticator;
+    }
+}
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
new file mode 100644
index 0000000..8138da6
--- /dev/null
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/SshShell.java
@@ -0,0 +1,184 @@
+/*******************************************************************************
+ * 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.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.HashMap;
+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 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;
+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.
+ *
+ */
+public class SshShell implements Command {
+	
+	private CommandProcessor processor;
+	private BundleContext context;
+	private InputStream in;
+	private OutputStream out;
+	private ExitCallback callback;
+	private Thread thread;
+	
+	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 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 int ADD_USER_COUNTER_LIMIT = 2;
+	
+	public SshShell(CommandProcessor processor, BundleContext context) {
+		this.processor = processor;
+		this.context = context;
+		supportedEscapeSequences = new HashMap<String, TerminalTypeMappings> ();
+        supportedEscapeSequences.put("ANSI", new ANSITerminalTypeMappings());
+        supportedEscapeSequences.put("WINDOWS", new ANSITerminalTypeMappings());
+        supportedEscapeSequences.put("VT100", new VT100TerminalTypeMappings());
+        VT220TerminalTypeMappings vtMappings = new VT220TerminalTypeMappings();
+        supportedEscapeSequences.put("VT220", vtMappings);
+        supportedEscapeSequences.put("XTERM", vtMappings);
+        supportedEscapeSequences.put("VT320", new VT320TerminalTypeMappings());
+        supportedEscapeSequences.put("SCO", new SCOTerminalTypeMappings());
+        
+        currentMappings = supportedEscapeSequences.get(DEFAULT_TTYPE);
+        currentEscapesToKey = currentMappings.getEscapesToKey();
+	}
+
+	public void setInputStream(InputStream in) {
+		this.in = in;
+	}
+
+	public void setOutputStream(OutputStream out) {
+		this.out = out;
+	}
+
+	public void setErrorStream(OutputStream err) {
+		// do nothing
+	}
+
+	public void setExitCallback(ExitCallback callback) {
+		this.callback = callback;
+	}
+
+	public void start(Environment env) throws IOException {
+		String term = env.getEnv().get(TERMINAL_PROPERTY);
+		TerminalTypeMappings mapping = supportedEscapeSequences.get(term.toUpperCase());
+		if(mapping != null) {
+			currentMappings = mapping;
+			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(PROMPT, OSGI_PROMPT);
+        session.put(INPUT_SCANNER, consoleInputHandler.getScanner());
+        session.put(SSH_INPUT_SCANNER, inputHandler.getScanner());
+        ((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();
+	}
+
+	public void destroy() {
+		return;
+	}
+	
+	public void onExit() {
+		thread.interrupt();
+		callback.onExit(0);
+	}
+
+}
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
new file mode 100644
index 0000000..9971193
--- /dev/null
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/SshShellFactory.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * 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.util.HashSet;
+import java.util.Set;
+
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.sshd.common.Factory;
+import org.apache.sshd.server.Command;
+import org.osgi.framework.BundleContext;
+
+/**
+ *  Shell factory used by the SSH server to create a SSH shell
+ *
+ */
+public class SshShellFactory implements Factory<Command> {
+	
+	private CommandProcessor processor;
+	private BundleContext context;
+	private Set<SshShell> shells = new HashSet<SshShell>();
+	
+	public SshShellFactory(CommandProcessor processor, BundleContext context) {
+		this.processor = processor;
+		this.context = context;
+	}
+	
+	public Command create() {
+		SshShell shell = new SshShell(processor, context);
+		shells.add(shell);
+		return shell;
+	}
+	
+	public void exit() {
+		for(SshShell shell : shells) {
+			shell.onExit();
+		}
+	}
+}
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/UserAdminCommand.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/UserAdminCommand.java
new file mode 100644
index 0000000..4a57da6
--- /dev/null
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/ssh/UserAdminCommand.java
@@ -0,0 +1,447 @@
+/*******************************************************************************
+ * 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.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.felix.service.command.CommandSession;
+import org.apache.felix.service.command.Descriptor;
+import org.eclipse.equinox.console.common.Scanner;
+import org.eclipse.equinox.console.storage.DigestUtil;
+import org.eclipse.equinox.console.storage.SecureUserStore;
+import org.eclipse.equinox.console.supportability.ConsoleInputScanner;
+
+/**
+ * This class provides commands for administering users: adding, removing and listing users; setting or changing password;
+ * resetting password; adding and removing roles
+ * 
+ *
+ */
+public class UserAdminCommand {
+    private static final String INPUT_SCANNER = "INPUT_SCANNER";
+    private static final String SSH_INPUT_SCANNER = "SSH_INPUT_SCANNER";
+    private static final String DEFAULT_USER = "equinox";
+	private static final int MINIMAL_PASSWORD_LENGTH = 8;
+	private static final int PASSWORD_INPUT_TRIALS_LIMIT = 3;
+	
+	/**
+	 * Command for adding a user
+	 *  
+	 * @param args command line arguments in the format -username <username> -password <password> -roles <comma-separated list of user roles (optional)>
+	 * @throws Exception
+	 */
+	@Descriptor("Add user with password and roles")
+	public void addUser(@Descriptor("-username <username>\r\n-password <password>\r\n-roles <comma-separated list of user roles (optional)>") String[] args) throws Exception {
+		String username = null;
+		String password = null;
+		String roles = "";
+		
+		for (int i = 0; i < args.length; i++) {
+			if ("-username".equals(args[i]) && i < args.length - 1) {
+				username = args[i + 1];
+				i++;
+			} else if ("-password".equals(args[i]) && i < args.length - 1) {
+				password = args[i + 1];
+				i++;
+			} else if ("-roles".equals(args[i]) && i < args.length - 1) {
+				roles = args[i + 1];
+				i++;
+			}
+		}
+		
+		if (! validateUsername(username)) {
+			throw new Exception("Invalid username");
+		}
+				
+		if (password == null) {
+			throw new Exception("Password not specified");
+		}
+		
+		if (password.length() < MINIMAL_PASSWORD_LENGTH) {
+			throw new Exception("Password should be at least 8 symblos");
+		}
+		
+		SecureUserStore.putUser(username, DigestUtil.encrypt(password), roles);
+		
+		if(SecureUserStore.existsUser(DEFAULT_USER)) {
+			SecureUserStore.deleteUser(DEFAULT_USER);
+		}
+	}
+	
+	/**
+	 * Command for setting or changing the password of a user.
+	 * 
+	 * @param args command-line arguments in the format -username <username> -password <password>
+	 * @throws Exception
+	 */
+	@Descriptor("Set or change password")
+	public void setPassword(@Descriptor("-username <username>\r\n-password <password>") String[] args) throws Exception {
+		String username = null;
+		String password = null;
+		
+		for (int i = 0; i < args.length; i++) {
+			if ("-username".equals(args[i]) && i < args.length - 1) {
+				username = args[i + 1];
+				i++;
+			} else if ("-password".equals(args[i]) && i < args.length - 1) {
+				password = args[i + 1];
+				i++;
+			}
+		}
+		
+		if (! validateUsername(username)) {
+			throw new Exception("Invalid username");
+		}
+				
+		if (password == null) {
+			throw new Exception("Password not specified");
+		}
+		
+		if (password.length() < MINIMAL_PASSWORD_LENGTH) {
+			throw new Exception("Password should be at least 8 symblos");
+		}
+		
+		SecureUserStore.setPassword(username, DigestUtil.encrypt(password));
+	}
+	
+	/**
+	 * Command for adding a user. The command interactively asks for username, password and roles; the
+	 * input plain text password is encrypted before storing.
+	 * 
+	 * @param session 
+	 * @return true if the user was successfully added
+	 * 
+	 * @throws Exception
+	 */
+	@Descriptor("Add user with password and roles interactively")
+	public boolean addUser(final CommandSession session) throws Exception {
+
+		ConsoleInputScanner inputScanner = (ConsoleInputScanner) session.get(INPUT_SCANNER);
+		Scanner scanner = (Scanner) session.get(SSH_INPUT_SCANNER);
+
+		try {
+			// switch off the history so that username, password and roles will not be saved in console history
+			if (scanner != null) {
+				inputScanner.toggleHistoryEnabled(false);
+			}			
+			BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
+			String username = readUsername(reader);
+			if (!validateUsername(username)) {
+				System.out.println("Invalid username");
+				return false;
+			}
+			
+			if (SecureUserStore.existsUser(username)) {
+				System.out.println("Username already exists");
+				return false;
+			}
+
+			// switch off the echo so that the password will not be printed in the console
+			if (scanner != null) {
+				scanner.toggleEchoEnabled(false);
+			}
+			String password = readPassword(reader);
+			if (password == null){
+				return false;
+			}
+			if (scanner != null) {
+				scanner.toggleEchoEnabled(true);
+			}
+
+			String roles = readRoles(reader);
+			if (roles == null) {
+				return false;
+			}
+
+			SecureUserStore.putUser(username, DigestUtil.encrypt(password), roles);
+			
+			if(SecureUserStore.existsUser(DEFAULT_USER)) {
+				SecureUserStore.deleteUser(DEFAULT_USER);
+			}
+		} finally {
+			if (scanner != null) {
+				inputScanner.toggleHistoryEnabled(true);
+				scanner.toggleEchoEnabled(true);
+			}	
+		}
+
+		return true;
+	}
+	
+	@Descriptor("Delete user")
+	public void deleteUser(@Descriptor("username of the user to be deleted") String username) throws Exception {
+		if (SecureUserStore.existsUser(username)) {
+			SecureUserStore.deleteUser(username);
+		}
+	}
+	
+	/**
+	 * Command to remove the password for a user
+	 * 
+	 * @param username user to remove the password for
+	 * @throws Exception
+	 */
+	@Descriptor("Reset password")
+	public void resetPassword(@Descriptor("username of the user whose password will be reset") String username) throws Exception {
+		if (!SecureUserStore.existsUser(username)) {
+			throw new Exception("Such user does not exist");
+		}
+		
+		SecureUserStore.resetPassword(username);
+	}
+	
+	/**
+	 * Command to set or change the password for a user; the command asks interactively for the new password; the
+	 * input plain text password is encrypted before storing.
+	 * 
+	 * @param session 
+	 * @param username the user whose password will be changed
+	 * @throws Exception
+	 */
+	@Descriptor("Set or change password")
+	public void setPassword(final CommandSession session, @Descriptor("Username of the user whose password will be changed") String username) throws Exception {
+		if ("".equals(username)) {
+			System.out.println("Username not specified");
+			return;
+		}		
+		
+		if (!SecureUserStore.existsUser(username)) {
+			throw new Exception("Such user does not exist");
+		}
+
+		ConsoleInputScanner inputScanner = (ConsoleInputScanner) session.get(INPUT_SCANNER);
+		Scanner scanner = (Scanner) session.get(SSH_INPUT_SCANNER);
+
+		try {
+			// switch off echo and history so that the password is neither echoed to the console, nor saved in history
+			if (scanner != null) {
+				inputScanner.toggleHistoryEnabled(false);
+				scanner.toggleEchoEnabled(false);
+			}			
+
+			BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
+			String password = readPassword(reader);
+			if (password == null) {
+				return;
+			}
+			
+			SecureUserStore.setPassword(username, DigestUtil.encrypt(password));
+		} finally {
+			if (scanner != null) {
+				inputScanner.toggleHistoryEnabled(true);
+				scanner.toggleEchoEnabled(true);
+			}	
+		}
+	}
+	
+	/**
+	 * Command to add roles to a user
+	 * 
+	 * @param args command line arguments in the format -username <username>\r\n-roles <comma-separated list of roles to add>
+	 * @throws Exception
+	 */
+	@Descriptor("Add roles to user")
+	public void addRoles(@Descriptor("-username <username>\r\n-roles <comma-separated list of roles to add>") String[] args) throws Exception {
+		String username = null;
+		String roles = "";
+		
+		for (int i = 0; i < args.length; i++) {
+			if ("-username".equals(args[i]) && i < args.length - 1) {
+				username = args[i + 1];
+				i++;
+			} else if ("-roles".equals(args[i]) && i < args.length - 1) {
+				roles = args[i + 1];
+				i++;
+			}
+		}
+		
+		if (username == null) {
+			throw new Exception("Username not specified");
+		}
+		
+		if("".equals(roles)) {
+			return;
+		}
+		
+		if (!SecureUserStore.existsUser(username)) {
+			throw new Exception("Such user does not exist");
+		}
+							
+		SecureUserStore.addRoles(username, roles);
+	}
+	
+	/**
+	 * Command to remove roles for a particular user
+	 * 
+	 * @param args command line arguments in the format -username <username>\r\n-roles <comma-separated list of roles to remove>
+	 * @throws Exception
+	 */
+	@Descriptor("Remove user roles")
+	public void removeRoles(@Descriptor("-username <username>\r\n-roles <comma-separated list of roles to remove>") String[] args) throws Exception {
+		String username = null;
+		String roles = "";
+		
+		for (int i = 0; i < args.length; i++) {
+			if ("-username".equals(args[i]) && i < args.length - 1) {
+				username = args[i + 1];
+				i++;
+			} else if ("-roles".equals(args[i]) && i < args.length - 1) {
+				roles = args[i + 1];
+				i++;
+			}
+		}
+		
+		if (username == null) {
+			throw new Exception("Username not specified");
+		}
+		
+		if("".equals(roles)) {
+			return;
+		}
+		
+		if (!SecureUserStore.existsUser(username)) {
+			throw new Exception("Such user does not exist");
+		}
+		
+		SecureUserStore.removeRoles(username, roles);
+	}
+	
+	/**
+	 * Command to list available users
+	 * 
+	 * @throws Exception
+	 */
+	@Descriptor("Lists available users")
+	public void listUsers()	throws Exception {
+		
+		String[] users = SecureUserStore.getUserNames();
+		
+		if(users.length == 0) {
+			System.out.println("No users available");
+			return;
+		}
+		
+		for(String user : users) {
+			System.out.println(user);
+		}
+	}
+
+	private String readPassword(BufferedReader reader) {
+		String password = null;
+		int count = 0;
+
+		while (password == null && count < PASSWORD_INPUT_TRIALS_LIMIT){
+			System.out.print("password: ");
+			System.out.flush();
+
+			try {
+				password = reader.readLine();
+			} catch (IOException e) {
+				System.out.println("Error while reading password");
+				return null;
+			}
+
+
+			if (password == null || "".equals(password)) {
+				System.out.println("Password not specified");
+				password = null;
+			} else if (password.length() < MINIMAL_PASSWORD_LENGTH) {
+				System.out.println("Password should be at least 8 symblos");
+				password = null;
+			}
+			
+			count++;
+		}
+		
+		if (password == null) {
+			return null;
+		}
+
+		String passwordConfirmation = null;
+		count = 0;
+
+		while (passwordConfirmation == null && count < PASSWORD_INPUT_TRIALS_LIMIT){
+			System.out.print("Confirm password: ");
+			System.out.flush();
+
+			try {
+				passwordConfirmation = reader.readLine();
+				if (!password.equals(passwordConfirmation)) {
+					System.out.println("The passwords do not match!");
+					passwordConfirmation = null;
+				}
+			} catch (IOException e) {
+				System.out.println("Error while reading password");
+				return null;
+			}
+			
+			count++;
+		}
+		if (passwordConfirmation == null){
+			return null;
+		}
+		return password;
+	}
+	
+	private String readUsername (BufferedReader reader) {
+		System.out.print("username: ");
+		System.out.flush();
+		String username = null;
+
+		try {
+			username = reader.readLine();
+		} catch (IOException e) {
+			System.out.println("Error while reading username");
+			return null;
+		}
+
+		if (username == null || "".equals(username)) {
+			System.out.println("Username not specified");
+			return null;
+		}
+
+		return username;
+	}
+	
+	private String readRoles (BufferedReader reader){
+		//roles input validation
+		System.out.print("roles: ");
+		System.out.flush();
+		String roles = null;
+		try {
+			roles = reader.readLine();
+		} catch (IOException e) {
+			System.out.println("Error while reading roles");
+			return null;
+		}
+
+		if (roles == null) {
+			roles = "";
+		}
+		return roles;
+	}
+	
+	private static boolean validateUsername (String username){
+		if( username == null){
+			return false; 
+		}else{
+			Pattern allowedChars = Pattern.compile("[A-Za-z0-9_.]+");
+			Matcher matcher = allowedChars.matcher(username);
+			return matcher.matches();
+		}
+	}
+
+}
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/storage/DigestUtil.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/storage/DigestUtil.java
new file mode 100644
index 0000000..fec76ba
--- /dev/null
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/storage/DigestUtil.java
@@ -0,0 +1,69 @@
+package org.eclipse.equinox.console.storage;
+
+import java.security.MessageDigest;
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * This class provides utility method for one-way hashing of strings
+ * 
+ */
+public class DigestUtil {
+	private static final char[] HEX_CHARS = "0123456789abcdef".toCharArray();
+	private static final String MD5 = "MD5";
+	private static final String SHA1 = "SHA1";
+	
+	/**
+	 * Create a one-way hash of an input strings. First a MD5 hash of the input string
+	 * is calculated and appended to the string, and then the new string is hashed with SHA1  
+	 * 
+	 * @param originalText the string to be hashed
+	 * @return hashed string
+	 * @throws Exception
+	 */
+	public static String encrypt(String originalText)throws Exception{
+		try {
+			String password_salt = appendSalt(originalText);
+			byte[] sha_digest;
+
+			sha_digest = getDigest(password_salt.getBytes(), SHA1);
+			return asHex(sha_digest);
+		} catch (NoSuchAlgorithmException e) {
+			throw new Exception ("Encryption Failed!");
+		}		
+	}
+	
+	private static String appendSalt(String inputPassword) throws NoSuchAlgorithmException{
+		byte [] salt = getDigest(inputPassword.getBytes(), MD5);
+		return inputPassword + asHex(salt);
+	}
+	
+	//byte array into hexademical string
+    private static String asHex(byte[] buf)
+    {
+        char[] chars = new char[2 * buf.length];
+        for (int i = 0; i < buf.length; ++i)
+        {
+            chars[2 * i] = HEX_CHARS[(buf[i] & 0xF0) >>> 4];
+            chars[2 * i + 1] = HEX_CHARS[buf[i] & 0x0F];
+        }
+        return new String(chars);
+    }
+    
+    //generate digest byte[]  
+    private static byte[] getDigest(byte[] inputData, String algorithm) throws NoSuchAlgorithmException { 
+    	MessageDigest md = MessageDigest.getInstance(algorithm);
+    	md.update(inputData);
+        return md.digest();
+    }
+}
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/storage/SecureUserStore.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/storage/SecureUserStore.java
new file mode 100644
index 0000000..64b02c3
--- /dev/null
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/storage/SecureUserStore.java
@@ -0,0 +1,605 @@
+/*******************************************************************************
+ * 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.storage;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+
+/**
+ * This class implements a storage for users, passwords and roles. The data is stored in a
+ * properties-like file in the format /ssh/<username>/password=<password> and 
+ * /ssh/<username>/roles=<comma_separated_list_of_roles>
+ * 
+ *
+ */
+public class SecureUserStore {
+
+	private static final String USER_STORE_FILE_NAME = "org.eclipse.equinox.console.jaas.file";
+	private static final String PASSWORD_KEY = "password";
+	private static final String ROLES_KEY = "roles";
+	private static final String SSH_PREFIX = "/ssh";
+	private static final String DELIMITER = "/";
+	private static final int USERNAME_INDEX = 2;
+	private static final int KEY_ELEMENTS_COUNT = 4;
+	
+	/**
+	 * Gets the usernames of all users.
+	 * 
+	 * @return String array containing the usernames
+	 */
+	public static String[] getUserNames() {
+		String userFileLoc = null;
+		InputStream in = null;
+		
+		try {
+			userFileLoc = getFileLocation();
+			in = new FileInputStream(userFileLoc);
+			Properties users = null;
+			try {
+				users = populateUserStore(in);
+			} catch (IOException e) {
+				throw new IllegalArgumentException("Cannot load properties from file " + userFileLoc);
+			}
+			Set<String> userNames = new HashSet<String>();
+			for (String key : users.stringPropertyNames()) {
+				String[] parts = key.split(DELIMITER);
+				// since the key starts with DELIMITER, the first element of key.split(DELIMITER) is an empty string
+				// that is why the result is {"", "ssh", "<username>", "password"} or {"", "ssh", "<username>", "roles"}
+				if (parts.length < KEY_ELEMENTS_COUNT) {
+					continue;
+				}
+				userNames.add(parts[USERNAME_INDEX]);
+			}
+
+			return userNames.toArray(new String[0]);
+		
+		} catch (FileNotFoundException e) {
+			throw new IllegalArgumentException("File " + userFileLoc + " does not exist");
+		} finally {
+			if (in != null) {
+				try {
+					in.close();
+				} catch (IOException e) {
+					//do nothing
+				}
+			}
+		}
+	}
+	
+	public static String getPassword(String username) {
+		return getProperty(username, PASSWORD_KEY);
+	}
+	
+	public static String getRoles(String username) {
+		return getProperty(username, ROLES_KEY);
+	}
+	
+	/**
+	 * Stores a user entry to the store.
+	 * 
+	 * @param username the name of the user
+	 * @param password the password of the user
+	 * @param roles comma-separated list of the roles of the user
+	 */
+	public static void putUser(String username, String password, String roles) {
+		String userFileLoc = null;
+		InputStream in = null;
+		OutputStream out = null;
+		
+		try {
+			userFileLoc = getFileLocation();
+			in = new FileInputStream(userFileLoc);
+			Properties users = null; 
+			
+			try {
+				users = populateUserStore(in);
+			} catch (IOException e) {
+				throw new IllegalArgumentException("Cannot load properties from file " + userFileLoc);
+			}
+			
+			if (existsUser(username, users)){
+				throw new IllegalArgumentException("The user already exists!");
+			}
+			
+			if (roles == null) {
+				roles = "";
+			}
+			
+			String userPassKey = constructPropertyName(username, PASSWORD_KEY);
+			String userRolesKey = constructPropertyName(username, ROLES_KEY);
+			users.put(userPassKey, password);
+			users.put(userRolesKey, roles);
+			
+			out = new FileOutputStream(userFileLoc);
+			try {
+				users.store(out, null);
+			} catch (IOException e) {
+				throw new IllegalArgumentException("Cannot store properties in file " + userFileLoc);
+			}
+		} catch (FileNotFoundException e) {
+			throw new IllegalArgumentException("File " + userFileLoc + " does not exist");
+		} finally {
+			if (in != null) {
+				try {
+					in.close();
+				} catch (IOException e) {
+					//do nothing
+				}
+			}
+			
+			if (out != null) {
+				try {
+					out.close();
+				} catch (IOException e) {
+					// do nothing
+				}
+			}
+		}
+	}
+	
+	/**
+	 * Adds roles for a particular user
+	 * 
+	 * @param username user to add roles to 
+	 * @param roles comma-separated list of new roles for the user 
+	 */
+	public static void addRoles(String username, String roles) {
+		String userFileLoc = null;
+		InputStream in = null;
+		OutputStream out = null;
+		
+		try {
+			if (roles == null || roles.length() == 0) {
+				return;
+			}
+			
+			userFileLoc = getFileLocation();
+			in = new FileInputStream(userFileLoc);
+			Properties users = null; 
+			
+			try {
+				users = populateUserStore(in);
+			} catch (IOException e) {
+				throw new IllegalArgumentException("Cannot load properties from file " + userFileLoc);
+			}
+			
+			String userRolesKey = constructPropertyName(username, ROLES_KEY);
+			String currentRoles = (String)users.remove(userRolesKey);
+			Set<String> rolesSet = new HashSet<String>();
+			
+			if (currentRoles.length() > 0) {
+				for (String role : currentRoles.split(",")) {
+					rolesSet.add(role);
+				}
+			}
+			
+			for (String role : roles.split(",")) {
+				rolesSet.add(role);
+			}
+			
+			StringBuilder builder = new StringBuilder();
+			for (String role : rolesSet) {
+				builder.append(role);
+				builder.append(",");
+			}
+			builder.deleteCharAt(builder.lastIndexOf(","));
+			
+			users.put(userRolesKey, builder.toString());
+			
+			out = new FileOutputStream(userFileLoc);
+			try {
+				users.store(out, null);
+			} catch (IOException e) {
+				throw new IllegalArgumentException("Cannot store properties in file " + userFileLoc);
+			}
+		} catch (FileNotFoundException e) {
+			throw new IllegalArgumentException("File " + userFileLoc + " does not exist");
+		} finally {
+			if (in != null) {
+				try {
+					in.close();
+				} catch (IOException e) {
+					//do nothing
+				}
+			}
+			
+			if (out != null) {
+				try {
+					out.close();
+				} catch (IOException e) {
+					// do nothing
+				}
+			}
+		}
+	}
+	
+	/**
+	 * Removes roles from a user
+	 * 
+	 * @param username user to remove roles from 
+	 * @param rolesToRemove comma-separated list of roles to be removed
+	 */
+	public static void removeRoles(String username, String rolesToRemove) {
+		String userFileLoc = null;
+		InputStream in = null;
+		OutputStream out = null;
+		
+		try {
+			if(rolesToRemove == null || rolesToRemove.length() == 0) {
+				return;
+			}
+			
+			userFileLoc = getFileLocation();
+			in = new FileInputStream(userFileLoc);
+			Properties users = null; 
+			
+			try {
+				users = populateUserStore(in);
+			} catch (IOException e) {
+				throw new IllegalArgumentException("Cannot load properties from file " + userFileLoc);
+			}
+			
+			String userRolesKey = constructPropertyName(username, ROLES_KEY);
+			String currentRoles = (String)users.remove(userRolesKey);
+			Set<String> rolesSet = new HashSet<String>();
+			
+			for (String role : currentRoles.split(",")) {
+				rolesSet.add(role);
+			}
+			
+			for (String role : rolesToRemove.split(",")) {
+				rolesSet.remove(role);
+			}
+			
+			StringBuilder builder = new StringBuilder();
+			for (String role : rolesSet) {
+				builder.append(role);
+				builder.append(",");
+			}
+			builder.deleteCharAt(builder.lastIndexOf(","));
+			
+			users.put(userRolesKey, builder.toString());
+			
+			out = new FileOutputStream(userFileLoc);
+			try {
+				users.store(out, null);
+			} catch (IOException e) {
+				throw new IllegalArgumentException("Cannot store properties in file " + userFileLoc);
+			}
+		} catch (FileNotFoundException e) {
+			throw new IllegalArgumentException("File " + userFileLoc + " does not exist");
+		} finally {
+			if (in != null) {
+				try {
+					in.close();
+				} catch (IOException e) {
+					//do nothing
+				}
+			}
+			
+			if (out != null) {
+				try {
+					out.close();
+				} catch (IOException e) {
+					// do nothing
+				}
+			}
+		}
+	}
+	
+	/**
+	 * Removes an entry for the user from the store.
+	 * 
+	 * @param username user to be removed
+	 */
+	public static void deleteUser(String username) {
+		String userFileLoc = null;
+		InputStream in = null;
+		OutputStream out = null;
+		
+		try {
+			userFileLoc = getFileLocation();
+			in = new FileInputStream(userFileLoc);
+			Properties users = null; 
+			
+			try {
+				users = populateUserStore(in);
+			} catch (IOException e) {
+				throw new IllegalArgumentException("Cannot load properties from file " + userFileLoc);
+			}
+			
+			if (!existsUser(username, users)){
+				throw new IllegalArgumentException("The user does not exist!");
+			}
+			
+			for (String key : users.stringPropertyNames()) {
+				if (key.contains(DELIMITER + username + DELIMITER)) {
+					users.remove(key);
+				}
+			}
+			
+			out = new FileOutputStream(userFileLoc);
+			try {
+				users.store(out, null);
+			} catch (IOException e) {
+				throw new IllegalArgumentException("Cannot store properties in file " + userFileLoc);
+			}
+		} catch (FileNotFoundException e) {
+			throw new IllegalArgumentException("File " + userFileLoc + " does not exist");
+		} finally {
+			if (in != null) {
+				try {
+					in.close();
+				} catch (IOException e) {
+					//do nothing
+				}
+			}
+			
+			if (out != null) {
+				try {
+					out.close();
+				} catch (IOException e) {
+					// do nothing
+				}
+			}
+		}
+	}
+	
+	/**
+	 * Removes the password for a user
+	 * 
+	 * @param username user to reset the password
+	 */
+	public static void resetPassword(String username) {
+		String userFileLoc = null;
+		InputStream in = null;
+		OutputStream out = null;
+		
+		try {
+			userFileLoc = getFileLocation();
+			in = new FileInputStream(userFileLoc);
+			Properties users = null; 
+			
+			try {
+				users = populateUserStore(in);
+			} catch (IOException e) {
+				throw new IllegalArgumentException("Cannot load properties from file " + userFileLoc);
+			}
+			
+			if (!existsUser(username, users)){
+				throw new IllegalArgumentException("The user does not exist!");
+			}
+			
+			for (String key : users.stringPropertyNames()) {
+				if (key.contains(DELIMITER + username + DELIMITER + PASSWORD_KEY)) {
+					users.remove(key);
+					break;
+				}
+			}
+			
+			out = new FileOutputStream(userFileLoc);
+			try {
+				users.store(out, null);
+			} catch (IOException e) {
+				throw new IllegalArgumentException("Cannot store properties in file " + userFileLoc);
+			}
+		} catch (FileNotFoundException e) {
+			throw new IllegalArgumentException("File " + userFileLoc + " does not exist");
+		} finally {
+			if (in != null) {
+				try {
+					in.close();
+				} catch (IOException e) {
+					//do nothing
+				}
+			}
+			
+			if (out != null) {
+				try {
+					out.close();
+				} catch (IOException e) {
+					// do nothing
+				}
+			}
+		}
+	}
+	
+	/**
+	 * Sets or changes the password for a user 
+	 * 
+	 * @param username user to set tha password for
+	 * @param password the new password
+	 */
+	public static void setPassword(String username, String password) {
+		String userFileLoc = null;
+		InputStream in = null;
+		OutputStream out = null;
+		
+		try {
+			userFileLoc = getFileLocation();
+			in = new FileInputStream(userFileLoc);
+			Properties users = null; 
+			
+			try {
+				users = populateUserStore(in);
+			} catch (IOException e) {
+				throw new IllegalArgumentException("Cannot load properties from file " + userFileLoc);
+			}
+			
+			if (!existsUser(username, users)){
+				throw new IllegalArgumentException("The user does not exist!");
+			}
+			
+			String passwordPropertyName = constructPropertyName(username, PASSWORD_KEY);
+			for (String key : users.stringPropertyNames()) {
+				if (key.contains(passwordPropertyName)) {
+					users.remove(key);
+					break;
+				}
+			}
+			
+			users.put(passwordPropertyName, password);
+			
+			out = new FileOutputStream(userFileLoc);
+			try {
+				users.store(out, null);
+			} catch (IOException e) {
+				throw new IllegalArgumentException("Cannot store properties in file " + userFileLoc);
+			}
+		} catch (FileNotFoundException e) {
+			throw new IllegalArgumentException("File " + userFileLoc + " does not exist");
+		} finally {
+			if (in != null) {
+				try {
+					in.close();
+				} catch (IOException e) {
+					//do nothing
+				}
+			}
+			
+			if (out != null) {
+				try {
+					out.close();
+				} catch (IOException e) {
+					// do nothing
+				}
+			}
+		}
+	}
+	
+	/**
+	 * CHecks if an entry for a user exists in the store
+	 *  
+	 * @param username user to check
+	 * @return true if there is an entry for this user in the store, false otherwise
+	 */
+	public static boolean existsUser(String username) {
+		String userFileLoc = null;
+		InputStream in = null;
+		try {
+			userFileLoc = getFileLocation();
+			in = new FileInputStream(userFileLoc);
+			Properties users = null; 
+			
+			try {
+				users = populateUserStore(in);
+			} catch (IOException e) {
+				throw new IllegalArgumentException("Cannot load properties from file " + userFileLoc);
+			}
+			
+			return existsUser(username, users);
+		} catch (FileNotFoundException e) {
+			throw new IllegalArgumentException("File " + userFileLoc + " does not exist");
+		} finally {
+			if (in != null) {
+				try {
+					in.close();
+				} catch (IOException e) {
+					//do nothing
+				}
+			}
+		}
+	}
+	
+	/**
+	 * Creates the store file if it does not exist
+	 * 
+	 * @throws IOException
+	 */
+	public static void initStorage() throws IOException {
+		String userFileLoc = getFileLocation();
+		File file = new File(userFileLoc);
+		if (!file.exists()) {
+			OutputStream out = null;
+			try {
+				Properties props = new Properties();
+				out = new FileOutputStream(file);
+				props.store(out, null);
+			} finally {
+				if (out != null) {
+					out.close();
+				}
+			}
+		}
+	}
+	
+	private static String getProperty(String username, String propertyName) {
+		String userFileLoc = null;
+		InputStream in = null;
+		try {
+			userFileLoc = getFileLocation();
+			in = new FileInputStream(userFileLoc);
+			Properties users = null; 
+			
+			try {
+				users = populateUserStore(in);
+			} catch (IOException e) {
+				throw new IllegalArgumentException("Cannot load properties from file " + userFileLoc);
+			}
+			
+			return users.getProperty(constructPropertyName(username, propertyName));
+		} catch (FileNotFoundException e) {
+			throw new IllegalArgumentException("File " + userFileLoc + " does not exist");
+		} finally {
+			if (in != null) {
+				try {
+					in.close();
+				} catch (IOException e) {
+					//do nothing
+				}
+			}
+		}
+	}
+	
+	private static Properties populateUserStore(InputStream in) throws IOException {
+		Properties userProperties = new Properties();
+		userProperties.load(in);
+		return userProperties;
+	}
+	
+	private static String getFileLocation(){
+		String userFileLoc = System.getProperty(USER_STORE_FILE_NAME);
+		if (userFileLoc == null) {
+			throw new IllegalArgumentException("Property " + USER_STORE_FILE_NAME + " is not set; cannot use JAAS authentication");
+		}
+		
+		return userFileLoc;
+	}
+	
+	private static String constructPropertyName(String user, String propertyName) {
+		StringBuilder builder = new StringBuilder();
+		builder.append(SSH_PREFIX);
+		builder.append(DELIMITER);
+		builder.append(user);
+		builder.append(DELIMITER);
+		builder.append(propertyName);
+		return builder.toString();
+	}
+	
+	private static boolean existsUser(String username, Properties users) {
+		for (String user : users.stringPropertyNames()) {
+			if (user.contains(DELIMITER + username + DELIMITER)) {
+				 return true;
+			}
+		}
+		return false;
+	}
+	
+}
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/supportability/ConsoleInputHandler.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/supportability/ConsoleInputHandler.java
index 58adcef..45add08 100644
--- a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/supportability/ConsoleInputHandler.java
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/supportability/ConsoleInputHandler.java
@@ -13,6 +13,7 @@
 
 import java.io.InputStream;
 import java.io.OutputStream;
+
 import org.eclipse.equinox.console.common.ConsoleInputStream;
 import org.eclipse.equinox.console.common.InputHandler;
 
@@ -21,9 +22,9 @@
  * which provides command line editing.
  */
 public class ConsoleInputHandler extends InputHandler {
+	
     public ConsoleInputHandler(InputStream input, ConsoleInputStream in, OutputStream out) {
         super(input, in, out);
         inputScanner = new ConsoleInputScanner(in, out);
-    }
-    
+    } 
 }
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/supportability/ConsoleInputScanner.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/supportability/ConsoleInputScanner.java
index 4ba48ed..e9e47fb 100644
--- a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/supportability/ConsoleInputScanner.java
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/supportability/ConsoleInputScanner.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2010 SAP AG
+ * Copyright (c) 2010, 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
@@ -11,31 +11,59 @@
 
 package org.eclipse.equinox.console.supportability;
 
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.service.command.CommandSession;
 import org.eclipse.equinox.console.common.ConsoleInputStream;
 import org.eclipse.equinox.console.common.KEYS;
 import org.eclipse.equinox.console.common.Scanner;
 import org.eclipse.equinox.console.common.SimpleByteBuffer;
+import org.eclipse.equinox.console.completion.CompletionHandler;
+import org.osgi.framework.BundleContext;
 
-import java.io.IOException;
-import java.io.OutputStream;
 /**
  * This class performs the processing of the input special characters,
  * and updates respectively what is displayed in the output. It handles
- * escape sequences, delete, backspace, arrows, insert, home, end, pageup, pagedown.
+ * escape sequences, delete, backspace, arrows, insert, home, end, pageup, pagedown, tab completion.
  */
 public class ConsoleInputScanner extends Scanner {
+
+	private static final byte TAB = 9;
     private boolean isCR = false;
     private boolean replace = false;
+    private boolean isCompletionMode = false;
+    // shows if command history should be saved - it is turned off in cases when passwords are to be entered
+    private boolean isHistoryEnabled = true;
 
     private final HistoryHolder history;
     private final SimpleByteBuffer buffer;
+    private CommandSession session;
+    private BundleContext context;
+    private Candidates candidates;
+    private int originalCursorPos;
 
-    public ConsoleInputScanner(ConsoleInputStream toShell, OutputStream toTelnet/*, BundleContext context*/) {
+    public ConsoleInputScanner(ConsoleInputStream toShell, OutputStream toTelnet) {
         super(toShell, toTelnet);
         history = new HistoryHolder();
         buffer = new SimpleByteBuffer();
     }
+    
+    public void toggleHistoryEnabled(boolean isEnabled) {
+    	isHistoryEnabled = isEnabled;
+    }
+    
+    public void setSession(CommandSession session) {
+    	this.session = session; 
+    }
 
+    public void setContext(BundleContext context) {
+    	this.context = context;
+    }
+    
     public void scan(int b) throws IOException {
         b &= 0xFF;
         if (isCR) {
@@ -44,11 +72,27 @@
                 return;
             }
         }
+        
+        if (b != TAB) {
+    		if (isCompletionMode == true) {
+    			isCompletionMode = false;
+    			candidates = null;
+    			originalCursorPos = 0;
+    		}
+    	}
+        
         if (isEsc) {
             scanEsc(b);
         } else {
-        	if(b == BACKSPACE) {
+            if (b == getBackspace()) {
         		backSpace();
+        	} else if(b == TAB) {
+        		if (isCompletionMode == false) {
+        			isCompletionMode = true;
+        			processTab();
+        		} else {
+        			processNextTab();
+        		}
         	} else if (b == CR) {
         		isCR = true;
         		processData();
@@ -56,7 +100,7 @@
         		processData();
         	} else if (b == ESC) {
         		startEsc();
-        	} else if (b == DEL) {
+            } else if (b == getDel()) {
         		delete();
         	} else {
         		if (b >= SPACE && b < MAX_CHAR) {
@@ -123,15 +167,169 @@
             }
         }
     }
+    
+    protected void processTab() throws IOException {
+    	CompletionHandler completionHandler = new CompletionHandler(context, session);
+    	Map<String, Integer> completionCandidates = completionHandler.getCandidates(buffer.copyCurrentData(), buffer.getPos());
+    	
+    	if (completionCandidates.size() == 1) {	
+            completeSingleCandidate(completionCandidates);   
+            isCompletionMode = false;
+            return;
+        }
+    	printNewLine();
+        if (completionCandidates.size() == 0) {
+            printCompletionError();
+            isCompletionMode = false;
+        } else {
+        	processCandidates(completionCandidates);
+        }
+        printNewLine();
+        printPrompt();
+    }
+    
+    protected void processCandidates(Map<String, Integer> completionCandidates) throws IOException{
+    	Set<String> candidatesNamesSet = completionCandidates.keySet();
+        String[] candidatesNames = (candidatesNamesSet.toArray(new String[0]));
+    	originalCursorPos = buffer.getPos();
+    	String[] candidateSuffixes = new String[candidatesNames.length];
+        for (int i = 0; i < candidatesNames.length; i++) {
+        	String candidateName = candidatesNames[i];
+        	candidateSuffixes[i] = getCandidateSuffix(candidateName, completionCandidates.get(candidateName), originalCursorPos);
+            for (byte symbol : candidateName.getBytes()) {
+                echo(symbol);
+            }
+            printNewLine();
+        }
+        
+        String commonPrefix = getCommonPrefix(candidateSuffixes);
+        candidates = new Candidates(removeCommonPrefix(candidateSuffixes, commonPrefix));
+        printString(commonPrefix, false);
+        originalCursorPos = buffer.getPos();
+    }
+    
+    protected void processNextTab() throws IOException {
+    	if (candidates == null) {
+    		return;
+    	}
+    	
+    	while (originalCursorPos < buffer.getPos()) {
+			backSpace();
+    	}
+    	
+    	String candidate = candidates.getCurrent();
+    	if(!candidate.equals("")) {
+    		printString(candidate, true);
+    	}
+    }
+    
+    protected void printCandidate(String candidate, int startIndex, int completionIndex) throws IOException {
+    	String suffix = getCandidateSuffix(candidate, startIndex, completionIndex);
+    	if(suffix.equals("")) {
+    		return;
+    	}
+        printString(suffix, true);
+    }
+    
+    protected void printString(String st, boolean isEcho) throws IOException {
+    	for (byte symbol : st.getBytes()) {
+            buffer.insert(symbol);
+            if (isEcho){
+            	echo(symbol);
+            }
+        }
+        flush();
+    }
+    
+    protected String getCommonPrefix(String[] names) {
+    	if (names.length == 0) {
+    		return "";
+    	}
+    	
+    	if (names.length == 1) {
+    		return names[0];
+    	}
+    	
+    	StringBuilder builder = new StringBuilder();
+    	char[] name = names[0].toCharArray();
+    	for(char c : name) {
+    		String prefix = builder.append(c).toString();
+    		for (int i = 1; i < names.length; i ++) {
+    			if (!names[i].startsWith(prefix)) {
+    				return prefix.substring(0, prefix.length() - 1);
+    			}
+    		}
+    	}
+    	
+    	return builder.toString();
+    }
+    
+    protected String[] removeCommonPrefix(String [] names, String commonPrefix){
+    	ArrayList<String> result = new ArrayList<String>();
+    	for (String name : names) {
+    		String nameWithoutPrefix = name.substring(commonPrefix.length());
+    		if (nameWithoutPrefix.length() > 0) {
+    			result.add(nameWithoutPrefix);
+    		}
+    	}
+    	result.add("");
+    	return result.toArray(new String[0]);
+    }
+    
+    protected String getCandidateSuffix(String candidate, int startIndex, int completionIndex) {
+    	int partialLength = completionIndex - startIndex;
+        if (partialLength >= candidate.length()) {
+        	return "";
+        }
+        return candidate.substring(partialLength);
+    }
+    
+    protected void completeSingleCandidate(Map<String, Integer> completionCandidates) throws IOException {
+    	Set<String> keys = completionCandidates.keySet();
+        String key = (keys.toArray(new String[0]))[0];
+        int startIndex = completionCandidates.get(key);
+        printCandidate(key, startIndex, buffer.getPos()); 
+    }
+    
+    protected void printCompletionError() throws IOException {
+    	byte[] curr = buffer.getCurrentData();
+        if (isHistoryEnabled == true) {
+        	history.add(curr);
+        }
+        
+        String errorMessage = "No completion available";
+        for (byte symbol : errorMessage.getBytes()) {
+            echo(symbol);
+        }
+    }
+    
+    protected void printNewLine() throws IOException{
+    	echo(CR);
+        echo(LF);
+        flush();
+    }
+    
+    protected void printPrompt() throws IOException{
+    	echo('o');
+        echo('s');
+        echo('g');
+        echo('i');
+        echo('>');
+        echo(SPACE);
+        echoBuff();
+        flush();
+    }
 
     private void processData() throws IOException {
-        buffer.add(CR);
+//        buffer.add(CR);
         buffer.add(LF);
         echo(CR);
         echo(LF);
         flush();
         byte[] curr = buffer.getCurrentData();
-        history.add(curr);
+        if (isHistoryEnabled == true) {
+        	history.add(curr);
+        }
         toShell.add(curr);
     }
 
@@ -284,4 +482,21 @@
         echoBuff();
         flush();
     }
+    
+    private static class Candidates {
+    	private String[] candidates;
+    	private int currentCandidateIndex = 0;
+    	
+    	public Candidates(String[] candidates) {
+    		this.candidates = candidates.clone();
+    	}
+    	
+    	public String getCurrent() {
+    		if (currentCandidateIndex >= candidates.length) {
+    			currentCandidateIndex = 0;
+    		}
+    		
+    		return candidates[currentCandidateIndex++];
+    	}
+    }
 }
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/supportability/HistoryHolder.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/supportability/HistoryHolder.java
index f1559f0..52bdd42 100644
--- a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/supportability/HistoryHolder.java
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/supportability/HistoryHolder.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2010 SAP AG
+ * Copyright (c) 2010, 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
@@ -18,6 +18,7 @@
  * A helper class, which implements history.
  */
 public class HistoryHolder {
+
     private static final int MAX = 100;
     private final byte[][] history;
     private int size;
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/Callback.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/Callback.java
index 96abb44..2874b3c 100644
--- a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/Callback.java
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/Callback.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2010 SAP AG
+ * Copyright (c) 2010, 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
@@ -12,5 +12,6 @@
 package org.eclipse.equinox.console.telnet;
 
 public interface Callback {
+	
 	public void finished();
 }
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/NegotiationFinishedCallback.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/NegotiationFinishedCallback.java
index 5f92d55..c468118 100644
--- a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/NegotiationFinishedCallback.java
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/NegotiationFinishedCallback.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2010 SAP AG
+ * Copyright (c) 2010, 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
@@ -17,6 +17,7 @@
  * This is necessary, because the user input should be interpreted with the correct terminal type.
  */
 public class NegotiationFinishedCallback implements Callback {
+	
 	private TelnetConnection telnetConnection;
 	
 	public NegotiationFinishedCallback(TelnetConnection telnetConnection) {
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 7689d38..9b1028f 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
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2010 SAP AG
+ * Copyright (c) 2010, 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
@@ -11,6 +11,10 @@
 
 package org.eclipse.equinox.console.telnet;
 
+import java.net.BindException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
 import org.apache.felix.service.command.CommandProcessor;
 import org.apache.felix.service.command.Descriptor;
 import org.osgi.framework.BundleContext;
@@ -19,18 +23,20 @@
  * This class implements a command for starting/stopping a simple telnet server.
  *
  */
-public class TelnetCommand
-{    
+public class TelnetCommand {
+	
 	private String defaultHost = null;
     private int defaultPort;
     private final CommandProcessor processor;
+    private final BundleContext context;
     private String host = null;
     private int port;
     private TelnetServer telnetServer;
 
-    public TelnetCommand(CommandProcessor procesor, BundleContext context)
+    public TelnetCommand(CommandProcessor processor, BundleContext context)
     {
-        this.processor = procesor;
+        this.processor = processor;
+        this.context = context;
         String telnetPort = null;
         String consolePropValue = context.getProperty("osgi.console");
         if(consolePropValue != null) {
@@ -44,11 +50,24 @@
         	try {
         		defaultPort = Integer.parseInt(telnetPort);
 			} catch (NumberFormatException e) {
-				defaultPort = 2223;
+				// do nothing
 			}
-        } else {
-        	defaultPort = 2223;
-        }
+        } 
+    }
+    
+    public synchronized void start() {
+    	Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put("osgi.command.scope", "equinox");
+		properties.put("osgi.command.function", new String[] {"telnet"});
+		if (port > 0 || defaultPort > 0) {
+			try{
+				telnet(new String[]{"start"});
+			} catch (Exception e) {
+				System.out.println("Cannot start telnet. Reason: " + e.getMessage());
+				e.printStackTrace();
+			}
+		}
+		context.registerService(TelnetCommand.class.getName(), this, properties);
     }
 
     @Descriptor("start/stop a telnet server")
@@ -87,6 +106,10 @@
         	port = defaultPort;
         }
         
+        if (port == 0) {
+        	throw new Exception("No telnet port specified");
+        }
+        
         if (newHost != null) {
         	host = newHost;
         } else {
@@ -98,7 +121,12 @@
                 throw new IllegalStateException("telnet is already running on port " + port);
             }
             
-            telnetServer = new TelnetServer(processor, host, port);
+            try {
+				telnetServer = new TelnetServer(context, processor, host, port);
+			} catch (BindException e) {
+				throw new Exception("Port " + port + " already in use");
+			}
+			
             telnetServer.setName("equinox telnet");
             telnetServer.start();    
         } else if ("stop".equals(command)) {
@@ -115,7 +143,7 @@
     	StringBuffer help = new StringBuffer();
     	help.append("telnet - start simple telnet server");
     	help.append("\n");
-    	help.append("Usage: telnet start | stop [-port port]");
+    	help.append("Usage: telnet start | stop [-port port] [-host host]");
     	help.append("\n");
     	help.append("\t");
     	help.append("-port");
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/TelnetConnection.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/TelnetConnection.java
index 29fb502..56a0e08 100644
--- a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/TelnetConnection.java
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/TelnetConnection.java
@@ -18,6 +18,8 @@
 import org.apache.felix.service.command.CommandSession;
 import org.eclipse.equinox.console.common.ConsoleInputStream;
 import org.eclipse.equinox.console.supportability.ConsoleInputHandler;
+import org.eclipse.equinox.console.supportability.ConsoleInputScanner;
+import org.osgi.framework.BundleContext;
 
 /**
  * This class manages a telnet connection. It is responsible for wrapping the original io streams
@@ -25,16 +27,23 @@
  *
  */
 public class TelnetConnection extends Thread {
+	
 	private Socket socket;
 	private CommandProcessor processor;
-	private boolean isTelnetNegotiationFinished = false;
+	private BundleContext context;
+	protected boolean isTelnetNegotiationFinished = false;
     private Callback callback;
-    private static final long WAIT_INTERVAL = 1000;
+    private static final long TIMEOUT = 1000;
     private static final long NEGOTIATION_TIMEOUT = 60000;
+    private static final String PROMPT = "prompt";
+    private static final String OSGI_PROMPT = "osgi> ";
+    private static final String INPUT_SCANNER = "INPUT_SCANNER";
+    private static final String SSH_INPUT_SCANNER = "SSH_INPUT_SCANNER";
 	
-	public TelnetConnection (Socket socket, CommandProcessor processor) {
+	public TelnetConnection (Socket socket, CommandProcessor processor, BundleContext context) {
 		this.socket = socket;
 		this.processor = processor;
+		this.context = context;
 		callback = new NegotiationFinishedCallback(this);
 	}
 	
@@ -48,16 +57,15 @@
 			
 			long start = System.currentTimeMillis();
 			
-			while(isTelnetNegotiationFinished == false && System.currentTimeMillis() - start < NEGOTIATION_TIMEOUT) {
-				synchronized (this) {
+			synchronized (this) {
+				while (isTelnetNegotiationFinished == false && System.currentTimeMillis() - start < NEGOTIATION_TIMEOUT) {
 					try {
-						wait(WAIT_INTERVAL);
+						wait(TIMEOUT);
 					} catch (InterruptedException e) {
 						// do nothing
 					}
 				}
 			}
-			
 			final CommandSession session;
 			PrintStream output = new PrintStream(out);
 			
@@ -68,11 +76,16 @@
 	        consoleInputHandler.getScanner().setDel(telnetInputHandler.getScanner().getDel());
 	        consoleInputHandler.getScanner().setCurrentEscapesToKey(telnetInputHandler.getScanner().getCurrentEscapesToKey());
 	        consoleInputHandler.getScanner().setEscapes(telnetInputHandler.getScanner().getEscapes());
+	        ((ConsoleInputScanner)consoleInputHandler.getScanner()).setContext(context);
 	        
 	        consoleInputHandler.start();
 	        
 	        session = processor.createSession(inp, output, output);
-			
+	        session.put(PROMPT, OSGI_PROMPT);
+	        session.put(INPUT_SCANNER, consoleInputHandler.getScanner());
+	        session.put(SSH_INPUT_SCANNER, telnetInputHandler.getScanner());
+	        ((ConsoleInputScanner)consoleInputHandler.getScanner()).setSession(session);
+	        
 			try {
 	            session.execute("gosh --login --noshutdown");
 	        } catch (Exception e) {
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/TelnetInputHandler.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/TelnetInputHandler.java
index f3dc957..0be072d 100644
--- a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/TelnetInputHandler.java
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/TelnetInputHandler.java
@@ -12,16 +12,18 @@
 package org.eclipse.equinox.console.telnet;
 
 import java.io.InputStream;
-import org.eclipse.equinox.console.common.*;
+import org.eclipse.equinox.console.common.ConsoleInputStream;
+import org.eclipse.equinox.console.common.ConsoleOutputStream;
+import org.eclipse.equinox.console.common.InputHandler;
 
 /**
  * This class customizes the generic handler with a concrete content processor,
  * which provides telnet protocol handling.
  */
 public class TelnetInputHandler extends InputHandler {
+	
     public TelnetInputHandler(InputStream input, ConsoleInputStream in, ConsoleOutputStream out, Callback callback) {
         super(input, in, out);
         inputScanner = new TelnetInputScanner(in, out, callback);
     }
-    
 }
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/TelnetInputScanner.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/TelnetInputScanner.java
index a14d8ba..57f9af3 100644
--- a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/TelnetInputScanner.java
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/TelnetInputScanner.java
@@ -11,15 +11,21 @@
  *******************************************************************************/
 package org.eclipse.equinox.console.telnet;
 
-import java.io.IOException;
-import java.util.*;
-import org.eclipse.equinox.console.common.*;
+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.Scanner;
+import org.eclipse.equinox.console.common.terminal.TerminalTypeMappings;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
 
 /**
  * This class performs the processing of the telnet commands,
  * and updates respectively what is displayed in the output. Also, it performs 
- * terminal type with the telnet client. This is important for some of the escape sequences, 
+ * terminal type negotiation with the telnet client. This is important for some of the escape sequences, 
  * which are different for the different terminal types. Without such negotiation,
  * some keys (such as backspace, del, insert, home, etc.) may not be correctly
  * interpreted by the telnet server. Currently the supported terminal types are
@@ -44,8 +50,8 @@
         TerminalTypeMappings currentMapping = supportedEscapeSequences.get(DEFAULT_TTYPE);
     	currentEscapesToKey = currentMapping.getEscapesToKey();
     	escapes = currentMapping.getEscapes();
-    	BACKSPACE = currentMapping.getBackspace();
-    	DEL = currentMapping.getDel();
+    	setBackspace(currentMapping.getBackspace());
+    	setDel(currentMapping.getDel());
     	this.callback = callback;
     }
     
@@ -105,10 +111,7 @@
     private static final int IS = 0;
 
     private boolean isNegotiation;
-//    private boolean isDo;
-//    private boolean isDont;
     private boolean isWill;
-//    private boolean isWont;
     
     private byte[] tTypeRequest = {(byte)IAC, (byte)SB, (byte)TTYPE, (byte)SEND, (byte)IAC, (byte)SE};
 
@@ -230,8 +233,8 @@
     	}
     	currentEscapesToKey = currentMapping.getEscapesToKey();
     	escapes = currentMapping.getEscapes();
-    	BACKSPACE = currentMapping.getBackspace();
-    	DEL = currentMapping.getDel();
+    	setBackspace(currentMapping.getBackspace());
+    	setDel(currentMapping.getDel());
     	if(callback != null) {
     		callback.finished();
     	}
diff --git a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/TelnetOutputStream.java b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/TelnetOutputStream.java
index 3222409..4d46494 100644
--- a/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/TelnetOutputStream.java
+++ b/console/org.eclipse.equinox.console.supportability/src/org/eclipse/equinox/console/telnet/TelnetOutputStream.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2010 SAP AG
+ * Copyright (c) 2010, 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
@@ -20,6 +20,7 @@
  * This class adds to the output stream wrapper initial negotiation of telnet communication.
  */
 public class TelnetOutputStream extends ConsoleOutputStream {
+	
     static final byte[] autoMessage = new byte[]{(byte) 255, (byte) 251, (byte) 1, // IAC WILL ECHO
                                                  (byte) 255, (byte) 251, (byte) 3, // IAC WILL SUPPRESS GO_AHEAD
                                                  (byte) 255, (byte) 253, (byte) 31, // IAC DO NAWS
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 768bdac..6304918 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
@@ -12,8 +12,12 @@
 package org.eclipse.equinox.console.telnet;
 
 import java.io.IOException;
-import java.net.*;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+
 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
@@ -21,11 +25,14 @@
  *
  */
 public class TelnetServer extends Thread {
+	
 	private ServerSocket server;
     private boolean isRunning = true;
     private CommandProcessor processor;
+    private BundleContext context;
     
-    public TelnetServer(CommandProcessor processor, String host, int port) throws IOException{
+    public TelnetServer(BundleContext context, CommandProcessor processor, String host, int port) throws IOException {
+    	this.context = context;
     	this.processor = processor;
     	if(host != null) {
     		server = new ServerSocket(port, 0, InetAddress.getByName(host));
@@ -41,7 +48,7 @@
             while (isRunning)
             {
                 final Socket socket = server.accept();
-                TelnetConnection telnetConnection = new TelnetConnection(socket, processor);
+                TelnetConnection telnetConnection = new TelnetConnection(socket, processor, context);
                 telnetConnection.start();
             }
         } catch (IOException e) {
diff --git a/console/org.eclipse.equinox.console.supportability/sshd-core-0.5.0.jar b/console/org.eclipse.equinox.console.supportability/sshd-core-0.5.0.jar
new file mode 100644
index 0000000..58bdc35
--- /dev/null
+++ b/console/org.eclipse.equinox.console.supportability/sshd-core-0.5.0.jar
Binary files differ