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