Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xbundles/org.eclipse.equinox.console.jaas.fragment/.classpath6
-rwxr-xr-xbundles/org.eclipse.equinox.console.jaas.fragment/.project28
-rwxr-xr-xbundles/org.eclipse.equinox.console.jaas.fragment/.settings/org.eclipse.jdt.core.prefs8
-rwxr-xr-xbundles/org.eclipse.equinox.console.jaas.fragment/META-INF/MANIFEST.MF8
-rwxr-xr-xbundles/org.eclipse.equinox.console.jaas.fragment/about.html28
-rwxr-xr-xbundles/org.eclipse.equinox.console.jaas.fragment/build.properties2
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/.classpath11
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/.gitignore1
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/.project28
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/.settings/org.eclipse.jdt.core.prefs8
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/META-INF/MANIFEST.MF12
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/about.html28
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/build.properties4
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/Activator.java35
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/command/adapter/ActivatorTests.java82
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/command/adapter/CommandProviderAdapterTest.java60
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/common/ConsoleInputStreamTests.java45
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/common/ConsoleOutputStreamTests.java41
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/common/HistoryHolderTests.java68
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/common/SimpleByteBufferTests.java96
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/completion/CommandLineParserTests.java86
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/completion/CommandNamesCompleterTests.java68
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/completion/CompletionHandlerTests.java165
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/completion/FileNamesCompleterTests.java97
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/completion/StringsCompleterTests.java62
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/completion/VariableNamesCompleterTests.java59
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/jaas/RolePrincipalTests.java45
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/jaas/UserPrincipalTests.java99
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/ssh/SshCommandTests.java188
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/ssh/SshCommandWithConfigAdminTests.java310
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/ssh/SshDisconnectCommandTests.java234
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/ssh/SshInputHandlerTests.java47
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/ssh/SshInputScannerTests.java67
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/ssh/SshShellTests.java152
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/ssh/UserAdminCommandTests.java119
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/storage/DigestUtilTests.java56
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/storage/SecureUserStoreTests.java109
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/supportability/ConsoleInputHandlerTests.java55
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/supportability/ConsoleInputScannerTests.java257
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/NegotiationFinishedCallbackTests.java27
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetCommandTests.java83
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetCommandWithConfigAdminTests.java196
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetConnectionTests.java99
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetDisconnectionTest.java103
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetInputHandlerTests.java49
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetInputScannerTests.java224
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetOutputStreamTests.java39
-rwxr-xr-xbundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetServerTests.java125
-rwxr-xr-xbundles/org.eclipse.equinox.console/.classpath7
-rwxr-xr-xbundles/org.eclipse.equinox.console/.project28
-rwxr-xr-xbundles/org.eclipse.equinox.console/.settings/org.eclipse.jdt.core.prefs8
-rwxr-xr-xbundles/org.eclipse.equinox.console/META-INF/MANIFEST.MF33
-rwxr-xr-xbundles/org.eclipse.equinox.console/about.html28
-rwxr-xr-xbundles/org.eclipse.equinox.console/build.properties5
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/command/adapter/Activator.java344
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/command/adapter/CommandProviderAdapter.java66
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/command/adapter/CustomCommandInterpreter.java258
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/ConsoleMessages.properties78
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/ConsoleMsg.java148
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/DisconnectCommand.java63
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/EquinoxCommandProvider.java1819
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/EquinoxCommandsConverter.java173
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/HelpCommand.java186
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/ManCommand.java43
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/Util.java208
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/ConsoleInputStream.java155
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/ConsoleOutputStream.java143
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/InputHandler.java68
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/KEYS.java31
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/Scanner.java146
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/SimpleByteBuffer.java148
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/terminal/ANSITerminalTypeMappings.java33
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/terminal/SCOTerminalTypeMappings.java33
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/terminal/TerminalTypeMappings.java71
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/terminal/VT100TerminalTypeMappings.java33
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/terminal/VT220TerminalTypeMappings.java27
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/terminal/VT320TerminalTypeMappings.java34
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/completion/CommandLineParser.java70
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/completion/CommandNamesCompleter.java63
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/completion/CompletionHandler.java91
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/completion/FileNamesCompleter.java108
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/completion/StringsCompleter.java70
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/completion/VariableNamesCompleter.java40
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/completion/common/Completer.java28
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/jaas/RolePrincipal.java67
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/jaas/SecureStorageLoginModule.java135
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/jaas/UserPrincipal.java140
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/ssh/SshCommand.java306
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/ssh/SshInputHandler.java30
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/ssh/SshInputScanner.java76
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/ssh/SshServ.java86
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/ssh/SshSession.java136
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/ssh/SshShell.java154
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/ssh/SshShellFactory.java63
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/ssh/UserAdminCommand.java447
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/storage/DigestUtil.java69
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/storage/SecureUserStore.java614
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/supportability/ConsoleInputHandler.java30
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/supportability/ConsoleInputScanner.java502
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/supportability/HistoryHolder.java100
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/Callback.java17
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/NegotiationFinishedCallback.java31
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/TelnetCommand.java244
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/TelnetConnection.java123
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/TelnetInputHandler.java29
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/TelnetInputScanner.java281
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/TelnetOutputStream.java41
-rwxr-xr-xbundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/TelnetServer.java123
-rwxr-xr-xbundles/org.eclipse.equinox.slf4j.stub/.classpath6
-rwxr-xr-xbundles/org.eclipse.equinox.slf4j.stub/.project28
-rwxr-xr-xbundles/org.eclipse.equinox.slf4j.stub/.settings/org.eclipse.jdt.core.prefs8
-rwxr-xr-xbundles/org.eclipse.equinox.slf4j.stub/.settings/org.eclipse.pde.core.prefs4
-rwxr-xr-xbundles/org.eclipse.equinox.slf4j.stub/META-INF/MANIFEST.MF7
-rwxr-xr-xbundles/org.eclipse.equinox.slf4j.stub/META-INF/p2.inf9
-rwxr-xr-xbundles/org.eclipse.equinox.slf4j.stub/about.html28
-rwxr-xr-xbundles/org.eclipse.equinox.slf4j.stub/build.properties14
116 files changed, 12654 insertions, 0 deletions
diff --git a/bundles/org.eclipse.equinox.console.jaas.fragment/.classpath b/bundles/org.eclipse.equinox.console.jaas.fragment/.classpath
new file mode 100755
index 000000000..bc74aabe3
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.jaas.fragment/.classpath
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.equinox.console.jaas.fragment/.project b/bundles/org.eclipse.equinox.console.jaas.fragment/.project
new file mode 100755
index 000000000..a6719df74
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.jaas.fragment/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.equinox.console.jaas.fragment</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/bundles/org.eclipse.equinox.console.jaas.fragment/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.equinox.console.jaas.fragment/.settings/org.eclipse.jdt.core.prefs
new file mode 100755
index 000000000..b47753e57
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.jaas.fragment/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+#Sat May 28 14:11:27 EEST 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/bundles/org.eclipse.equinox.console.jaas.fragment/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.console.jaas.fragment/META-INF/MANIFEST.MF
new file mode 100755
index 000000000..ee3f2e185
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.jaas.fragment/META-INF/MANIFEST.MF
@@ -0,0 +1,8 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: SSHD Fragment
+Bundle-SymbolicName: org.eclipse.equinox.console.jaas.fragment
+Bundle-Version: 1.0.0.qualifier
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+DynamicImport-Package: org.eclipse.equinox.console.jaas
+Fragment-Host: org.apache.sshd.core;bundle-version="0.5.0"
diff --git a/bundles/org.eclipse.equinox.console.jaas.fragment/about.html b/bundles/org.eclipse.equinox.console.jaas.fragment/about.html
new file mode 100755
index 000000000..bed8451cf
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.jaas.fragment/about.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+
+<p>May 28, 2011</p>
+<h3>License</h3>
+
+<p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 (&quot;EPL&quot;). A copy of the EPL is available
+at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is
+being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may
+apply to your use of any object code in the Content. Check the Redistributor's license that was
+provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p>
+
+</body>
+</html> \ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.console.jaas.fragment/build.properties b/bundles/org.eclipse.equinox.console.jaas.fragment/build.properties
new file mode 100755
index 000000000..a854d1dc8
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.jaas.fragment/build.properties
@@ -0,0 +1,2 @@
+bin.includes = META-INF/,\
+ about.html
diff --git a/bundles/org.eclipse.equinox.console.tests/.classpath b/bundles/org.eclipse.equinox.console.tests/.classpath
new file mode 100755
index 000000000..bb16802ac
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/.classpath
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
+ <classpathentry kind="lib" path="C:/Users/i043832/workspace_console/log4j-1.2.13.jar"/>
+ <classpathentry kind="lib" path="C:/Users/i043832/workspace_console/slf4j-api-1.5.11.jar"/>
+ <classpathentry kind="lib" path="C:/Users/i043832/workspace_console/slf4j-log4j12-1.5.11.jar"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.equinox.console.tests/.gitignore b/bundles/org.eclipse.equinox.console.tests/.gitignore
new file mode 100755
index 000000000..3b8360084
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/.gitignore
@@ -0,0 +1 @@
+/hostkey.ser
diff --git a/bundles/org.eclipse.equinox.console.tests/.project b/bundles/org.eclipse.equinox.console.tests/.project
new file mode 100755
index 000000000..7d67a8eb6
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.equinox.console.tests</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/bundles/org.eclipse.equinox.console.tests/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.equinox.console.tests/.settings/org.eclipse.jdt.core.prefs
new file mode 100755
index 000000000..507d6a6a1
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+#Tue Apr 12 16:03:12 EEST 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/bundles/org.eclipse.equinox.console.tests/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.console.tests/META-INF/MANIFEST.MF
new file mode 100755
index 000000000..04a2c18fc
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/META-INF/MANIFEST.MF
@@ -0,0 +1,12 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Console supportability tests
+Bundle-SymbolicName: org.eclipse.equinox.console.tests
+Bundle-Version: 1.0.0.qualifier
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Import-Package: junit.framework;version="4.8.1",
+ org.apache.sshd.client.future,
+ org.easymock;version="2.4.0",
+ org.junit;version="4.8.1"
+Fragment-Host: org.eclipse.equinox.console
diff --git a/bundles/org.eclipse.equinox.console.tests/about.html b/bundles/org.eclipse.equinox.console.tests/about.html
new file mode 100755
index 000000000..bed8451cf
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/about.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+
+<p>May 28, 2011</p>
+<h3>License</h3>
+
+<p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 (&quot;EPL&quot;). A copy of the EPL is available
+at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is
+being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may
+apply to your use of any object code in the Content. Check the Redistributor's license that was
+provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p>
+
+</body>
+</html> \ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.console.tests/build.properties b/bundles/org.eclipse.equinox.console.tests/build.properties
new file mode 100755
index 000000000..34d2e4d2d
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/build.properties
@@ -0,0 +1,4 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/Activator.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/Activator.java
new file mode 100755
index 000000000..e024b14fa
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/Activator.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * 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;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class Activator implements BundleActivator {
+ private static BundleContext context;
+
+ public void start(BundleContext context) throws Exception {
+ this.context = context;
+ }
+
+
+ public void stop(BundleContext context) throws Exception {
+ this.context = null;
+ }
+
+ public static BundleContext getContext() {
+ return context;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/command/adapter/ActivatorTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/command/adapter/ActivatorTests.java
new file mode 100755
index 000000000..d1c3ab37b
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/command/adapter/ActivatorTests.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * 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.command.adapter;
+
+import static org.junit.Assert.*;
+
+import java.lang.reflect.Method;
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.osgi.framework.console.CommandInterpreter;
+import org.eclipse.osgi.framework.console.CommandProvider;
+import org.junit.Test;
+
+public class ActivatorTests {
+
+ private static final String SCOPE_PROPERTY_NAME = "osgi.command.scope";
+ private static final String FUNCTION_PROPERTY_NAME = "osgi.command.function";
+ private static final String EQUINOX_SCOPE = "equinox";
+
+ @Test
+ public void testGetCommandMethods() {
+ Set<String> commandNames = new HashSet<String>();
+ commandNames.add("_testMethod1");
+ commandNames.add("_testMethod2");
+ commandNames.add("_testMethod3");
+
+ Activator activator = new Activator();
+ CommandProvider command = new TestCommandProvider();
+ Method[] methods = activator.getCommandMethods(command);
+
+ assertEquals("Command methods not as expected", 3, methods.length);
+ for (Method method : methods) {
+ assertTrue("Command methods should not include " + method.getName(), commandNames.contains(method.getName()));
+ }
+
+ Dictionary<String, Object> props = activator.getAttributes(methods);
+ assertTrue("Attributes should contain property " + SCOPE_PROPERTY_NAME + " with value " + EQUINOX_SCOPE, EQUINOX_SCOPE.equals(props.get(SCOPE_PROPERTY_NAME)));
+ String[] methodNames = (String[])props.get(FUNCTION_PROPERTY_NAME);
+ assertEquals("Methods number not as expected", methods.length, methodNames.length);
+
+ for(int i = 0; i < methods.length; i++) {
+ assertEquals("Wrong method name", methods[i].getName().substring(1), methodNames[i]);
+ }
+ }
+
+ class TestCommandProvider implements CommandProvider {
+ public void _testMethod1(CommandInterpreter i) {
+
+ }
+
+ public void _testMethod2(CommandInterpreter i) {
+
+ }
+
+ public void _testMethod3(CommandInterpreter i) {
+
+ }
+
+ private void _method(CommandInterpreter i) {
+
+ }
+
+ @Override
+ public String getHelp() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/command/adapter/CommandProviderAdapterTest.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/command/adapter/CommandProviderAdapterTest.java
new file mode 100755
index 000000000..b9c6c3f15
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/command/adapter/CommandProviderAdapterTest.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * 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.command.adapter;
+
+import static org.junit.Assert.*;
+
+import java.lang.reflect.Method;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.osgi.framework.console.CommandInterpreter;
+import org.eclipse.osgi.framework.console.CommandProvider;
+import org.junit.Test;
+
+public class CommandProviderAdapterTest {
+
+ @Test
+ public void testMain() throws Exception {
+ CommandProvider provider = new TestCommandProvider();
+ Method[] methods = TestCommandProvider.class.getMethods();
+ Set<Method> m = new HashSet<Method>();
+ for (Method method : methods) {
+ if (method.getName().startsWith("_")) {
+ m.add(method);
+ }
+ }
+ CommandProviderAdapter providerAdapter = new CommandProviderAdapter(provider, m.toArray(new Method[0]));
+
+ String result = (String) providerAdapter.main(new Object[] {"test"});
+ assertEquals("Result should be test", "test", result);
+
+ result = (String) providerAdapter.main(new Object[] {"echo", "hello"});
+ assertEquals("Result should be hello", "hello", result);
+ }
+
+ class TestCommandProvider implements CommandProvider {
+ public String _test(CommandInterpreter i) {
+ return "test";
+ }
+
+ public String _echo(CommandInterpreter i) {
+ return i.nextArgument();
+ }
+
+ @Override
+ public String getHelp() {
+ return "this is a test command provider";
+ }
+
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/common/ConsoleInputStreamTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/common/ConsoleInputStreamTests.java
new file mode 100755
index 000000000..b3732bbfc
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/common/ConsoleInputStreamTests.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * 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 contribution
+ ******************************************************************************/
+
+package org.eclipse.equinox.console.common;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.eclipse.equinox.console.common.ConsoleInputStream;
+
+public class ConsoleInputStreamTests {
+
+ private static final int DATA_LENGTH = 4;
+
+ @Test
+ public void addReadBufferTest() throws Exception {
+ ConsoleInputStream in = new ConsoleInputStream();
+ byte[] data = new byte[] { (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd' };
+ in.add(data);
+ byte[] read = new byte[DATA_LENGTH];
+ for (int i = 0; i < DATA_LENGTH; i++) {
+ in.read(read, i, 1);
+ Assert.assertEquals("Incorrect char read; position " + i + " expected: " + data[i] + ", actual: " + read[i], read[i], data[i]);
+ }
+ }
+
+ @Test
+ public void addReadTest() throws Exception {
+ ConsoleInputStream in = new ConsoleInputStream();
+ byte[] data = new byte[] { (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd' };
+ in.add(data);
+ for (int i = 0; i < DATA_LENGTH; i++) {
+ byte symbol = (byte) in.read();
+ Assert.assertEquals("Incorrect char read; position " + i + " expected: " + data[i] + ", actual: " + symbol, symbol, data[i]);
+ }
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/common/ConsoleOutputStreamTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/common/ConsoleOutputStreamTests.java
new file mode 100755
index 000000000..c1044ee8b
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/common/ConsoleOutputStreamTests.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * 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 contribution
+ ******************************************************************************/
+
+package org.eclipse.equinox.console.common;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+
+public class ConsoleOutputStreamTests {
+
+ private static final int DATA_LENGTH = 4;
+
+ @Test
+ public void testWrite() throws Exception {
+ ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+ ConsoleOutputStream out = new ConsoleOutputStream(byteOut);
+ byte[] data = new byte[] { 'a', 'b', 'c', 'd' };
+ for (byte b : data) {
+ out.write(b);
+ }
+ out.flush();
+ byte[] res = byteOut.toByteArray();
+
+ Assert.assertNotNull("Bytes not written; result null", res);
+ Assert.assertFalse("Bytes not written; result empty", res.length == 0);
+
+ for (int i = 0; i < DATA_LENGTH; i++) {
+ Assert.assertEquals("Wrong char read. Position " + i + ", expected " + data[i] + ", read " + res[i], data[i], res[i]);
+ }
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/common/HistoryHolderTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/common/HistoryHolderTests.java
new file mode 100755
index 000000000..c6321a63b
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/common/HistoryHolderTests.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * 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 contribution
+ ******************************************************************************/
+
+package org.eclipse.equinox.console.common;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.eclipse.equinox.console.supportability.HistoryHolder;
+
+public class HistoryHolderTests {
+
+ @Test
+ public void test() {
+ HistoryHolder historyHolder = new HistoryHolder();
+ byte[] line1 = new byte[] { 'a', 'b', 'c', 'd' };
+ byte[] line2 = new byte[] { 'x', 'y', 'z' };
+ byte[] line3 = new byte[] { 'k', 'l', 'm', 'n' };
+
+ historyHolder.add(line1);
+ historyHolder.add(line2);
+ historyHolder.add(line3);
+
+ byte[] first = historyHolder.first();
+ Assert.assertEquals("Wrong length of first member", line1.length, first.length);
+ Assert.assertArrayEquals("Wrong first member", line1, first);
+
+ byte[] last = historyHolder.last();
+ Assert.assertEquals("Wrong length of last member", line3.length, last.length);
+ Assert.assertArrayEquals("Wrong last member", line3, last);
+
+ byte[] prev = historyHolder.prev();
+ Assert.assertEquals("Wrong length of previous member", line2.length, prev.length);
+ Assert.assertArrayEquals("Wrong previous member", line2, prev);
+
+ byte[] next = historyHolder.next();
+ Assert.assertEquals("Wrong length of next member", line3.length, next.length);
+ Assert.assertArrayEquals("Wrong next member", line3, next);
+
+ historyHolder.first();
+ historyHolder.add(new byte[] {});
+ byte[] current = historyHolder.prev();
+ Assert.assertEquals("Wrong length of next member", line3.length, current.length);
+ Assert.assertArrayEquals("Wrong next member", line3, current);
+
+ historyHolder.first();
+ historyHolder.add(line1);
+ current = historyHolder.prev();
+ Assert.assertEquals("Wrong length of next member", line1.length, current.length);
+ Assert.assertArrayEquals("Wrong next member", line1, current);
+ Assert.assertArrayEquals("Second line should now be first", line2, historyHolder.first());
+
+ historyHolder.reset();
+ Assert.assertNull("History should be empty", historyHolder.first());
+ Assert.assertNull("History should be empty", historyHolder.last());
+ Assert.assertNull("History should be empty", historyHolder.next());
+ Assert.assertNull("History should be empty", historyHolder.prev());
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/common/SimpleByteBufferTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/common/SimpleByteBufferTests.java
new file mode 100755
index 000000000..9d4362431
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/common/SimpleByteBufferTests.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * 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 contribution
+ ******************************************************************************/
+
+package org.eclipse.equinox.console.common;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class SimpleByteBufferTests {
+
+ @Test
+ public void testBuffer() throws Exception {
+ SimpleByteBuffer buffer = new SimpleByteBuffer();
+ buffer.add('a');
+ buffer.add('b');
+ buffer.add('c');
+ buffer.add('d');
+
+ Assert.assertTrue("Wrong buffer size; expected 4, actual " + buffer.getSize(), buffer.getSize() == 4);
+
+ check(buffer, new byte[] { 'a', 'b', 'c', 'd' });
+
+ byte[] data = buffer.getCurrentData();
+ byte[] expected = new byte[] { 'a', 'b', 'c', 'd' };
+
+ Assert.assertTrue("Data not as expected: expected length " + expected.length + ", actual length " + data.length,
+ data.length == expected.length);
+
+ for (int i = 0; i < data.length; i++) {
+ Assert.assertEquals("Incorrect data read. Position " + i + ", expected " + expected[i] + ", read " + data[i], expected[i], data[i]);
+ }
+
+ buffer.insert('a');
+ buffer.insert('b');
+ buffer.insert('c');
+ buffer.insert('d');
+
+ int pos = buffer.getPos();
+ buffer.goLeft();
+ int newPos = buffer.getPos();
+ Assert.assertEquals("Error while moving left; old pos: " + pos + ", new pos: ", pos - 1, newPos);
+
+ buffer.insert('e');
+ check(buffer, new byte[] { 'a', 'b', 'c', 'e', 'd' });
+
+ buffer.goLeft();
+ buffer.delete();
+ check(buffer, new byte[] { 'a', 'b', 'c', 'd' });
+
+ pos = buffer.getPos();
+ buffer.goRight();
+ newPos = buffer.getPos();
+ Assert.assertEquals("Error while moving right; old pos: " + pos + ", new pos: ", pos + 1, newPos);
+
+ buffer.backSpace();
+ check(buffer, new byte[] { 'a', 'b', 'c' });
+
+ buffer.delAll();
+ Assert.assertTrue("Bytes in buffer not correctly deleted", (buffer.getSize() == 0) && (buffer.getPos() == 0));
+
+ buffer.set(new byte[] { 'a', 'b', 'c', 'd' });
+ check(buffer, new byte[] { 'a', 'b', 'c', 'd' });
+
+ data = buffer.copyCurrentData();
+ Assert.assertArrayEquals("Buffer copy does not work properly", new byte[] { 'a', 'b', 'c', 'd' }, data);
+
+ buffer.goLeft();
+ buffer.replace('e');
+ check(buffer, new byte[] { 'a', 'b', 'c', 'e' });
+
+ buffer.resetPos();
+ Assert.assertTrue("Resetting position does not work properly", buffer.getPos() == 0);
+
+ Assert.assertEquals("Wrong current char", 'a', buffer.getCurrentChar());
+ }
+
+ private void check(SimpleByteBuffer buffer, byte[] expected) throws Exception {
+ byte[] data = buffer.copyCurrentData();
+
+ Assert.assertTrue("Data not as expected: expected length " + expected.length + ", actual length " + data.length,
+ data.length == expected.length);
+
+ for (int i = 0; i < data.length; i++) {
+ Assert.assertEquals("Incorrect data read. Position " + i + ", expected " + expected[i] + ", read " + data[i], expected[i], data[i]);
+ }
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/completion/CommandLineParserTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/completion/CommandLineParserTests.java
new file mode 100755
index 000000000..408c8a8f1
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/completion/CommandLineParserTests.java
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * 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 static org.junit.Assert.*;
+
+import org.junit.Test;
+
+public class CommandLineParserTests {
+
+ private static final String PIPE_TEST_INPUT = "command1|comm";
+ private static final String CONSECUTIVE_COMMANDS_TEST_INPUT = "command1;comm";
+ private static final String ASSIGNMENT_TEST_INPUT = "var=val";
+ private static final String START_CLOSURE_TEST_INPUT = "${comm";
+ private static final String END_CLOSURE_TEST_INPUT = "${command}arg1";
+ private static final String START_MACRO_TEST_INPUT = "$(macr";
+ private static final String END_MACRO_TEST_INPUT = "$(macro)val";
+ private static final String VARIABLE_TEST_INPUT = "$VAR";
+ private static final String START_MAP_TEST_INPUT = "<key=val";
+ private static final String END_MAP_TEST_INPUT = "<key=val>other";
+ private static final String START_LIST_TEST_INPUT = "[elem1,elem2,el";
+ private static final String LIST_TEST_INPUT = "[elem1, elem2, elem3]other";
+ private static final String COMMAND_ARGUMENTS_TEST_INPUT = "command argument1 argum";
+ private static final String COMMAND_NAME_TEST_INPUT = "com";
+ private static final String COMMENT_TEST_INPUT="command#comment";
+
+ @Test
+ public void testGetCurrentToken() {
+ String token;
+
+ token = CommandLineParser.getCurrentToken(PIPE_TEST_INPUT, PIPE_TEST_INPUT.length());
+ assertEquals("Pipe not parsed correctly", "comm", token);
+
+ token = CommandLineParser.getCurrentToken(CONSECUTIVE_COMMANDS_TEST_INPUT, CONSECUTIVE_COMMANDS_TEST_INPUT.length());
+ assertEquals("Consequtive commands not parsed correctly", "comm", token);
+
+ token = CommandLineParser.getCurrentToken(ASSIGNMENT_TEST_INPUT, ASSIGNMENT_TEST_INPUT.length());
+ assertEquals("Assignment not parsed correctly", "val", token);
+
+ token = CommandLineParser.getCurrentToken(START_CLOSURE_TEST_INPUT, START_CLOSURE_TEST_INPUT.length());
+ assertEquals("Start closure not parsed correctly", "comm", token);
+
+ token = CommandLineParser.getCurrentToken(END_CLOSURE_TEST_INPUT, END_CLOSURE_TEST_INPUT.length());
+ assertEquals("End closure not parsed correctly", "arg1", token);
+
+ token = CommandLineParser.getCurrentToken(START_MACRO_TEST_INPUT, START_MACRO_TEST_INPUT.length());
+ assertEquals("Start macro not parsed correctly", "macr", token);
+
+ token = CommandLineParser.getCurrentToken(END_MACRO_TEST_INPUT, END_MACRO_TEST_INPUT.length());
+ assertEquals("End macro not parsed correctly", "val", token);
+
+ token = CommandLineParser.getCurrentToken(VARIABLE_TEST_INPUT, VARIABLE_TEST_INPUT.length());
+ assertEquals("Variable name not parsed correctly", "VAR", token);
+
+ token = CommandLineParser.getCurrentToken(START_MAP_TEST_INPUT, START_MAP_TEST_INPUT.length());
+ assertNull("Start map not parsed correctly", token);
+
+ token = CommandLineParser.getCurrentToken(END_MAP_TEST_INPUT, END_MAP_TEST_INPUT.length());
+ assertEquals("End map not parsed correctly", "other", token);
+
+ token = CommandLineParser.getCurrentToken(START_LIST_TEST_INPUT, START_LIST_TEST_INPUT.length());
+ assertNull("Start list not parsed correctly", token);
+
+ token = CommandLineParser.getCurrentToken(LIST_TEST_INPUT, LIST_TEST_INPUT.length());
+ assertEquals("List not parsed correctly", "other", token);
+
+ token = CommandLineParser.getCurrentToken(COMMAND_ARGUMENTS_TEST_INPUT, COMMAND_ARGUMENTS_TEST_INPUT.length());
+ assertEquals("Command with arguments not parsed correctly", "argum", token);
+
+ token = CommandLineParser.getCurrentToken(COMMAND_NAME_TEST_INPUT, COMMAND_NAME_TEST_INPUT.length());
+ assertEquals("Command name not parsed correctly", "com", token);
+
+ token = CommandLineParser.getCurrentToken(COMMENT_TEST_INPUT, COMMENT_TEST_INPUT.length());
+ assertNull("Comment not parsed correctly", token);
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/completion/CommandNamesCompleterTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/completion/CommandNamesCompleterTests.java
new file mode 100755
index 000000000..390ffa743
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/completion/CommandNamesCompleterTests.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * 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 static org.junit.Assert.*;
+import static org.easymock.EasyMock.*;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.service.command.CommandSession;
+import org.junit.Test;
+
+public class CommandNamesCompleterTests {
+
+ private static final String COMMANDS = ".commands";
+
+ @Test
+ public void testGetCandidates() {
+ Set<String> commands = new HashSet<String>();
+ commands.add("equinox:bundles");
+ commands.add("equinox:diag");
+ commands.add("equinox:setprop");
+ commands.add("gogo:lb");
+ commands.add("gogo:echo");
+ commands.add("gogo:set");
+
+ CommandSession session = createMock(CommandSession.class);
+ expect(session.get(COMMANDS)).andReturn(commands).times(4);
+ replay(session);
+
+ CommandNamesCompleter completer = new CommandNamesCompleter(session);
+ Map<String, Integer> candidates;
+
+ candidates = completer.getCandidates("se", 2);
+ assertNotNull("Candidates null", candidates);
+ assertEquals("Candidates not as expected", 2, candidates.size());
+ assertNotNull("set should be in the resultset, but it is not", candidates.get("set"));
+ assertNotNull("setprop should be in the resultset, but it is not", candidates.get("setprop"));
+
+ candidates = completer.getCandidates("equinox:bun", "equinox:bun".length());
+ assertNotNull("Candidates null", candidates);
+ assertEquals("Candidates not as expected", 1, candidates.size());
+ assertNotNull("equinox:bundles should be in the resultset, but it is not", candidates.get("equinox:bundles"));
+
+ candidates = completer.getCandidates("ec", 2);
+ assertNotNull("Candidates null", candidates);
+ assertEquals("Candidates not as expected", 1, candidates.size());
+ assertNotNull("echo should be in the resultset, but it is not", candidates.get("echo"));
+
+ candidates = completer.getCandidates("head", 4);
+ assertNotNull("Candidates null", candidates);
+ assertEquals("Candidates not as expected", 0, candidates.size());
+
+ verify(session);
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/completion/CompletionHandlerTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/completion/CompletionHandlerTests.java
new file mode 100755
index 000000000..e71be0c12
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/completion/CompletionHandlerTests.java
@@ -0,0 +1,165 @@
+/*******************************************************************************
+ * 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 static org.junit.Assert.*;
+import static org.easymock.EasyMock.*;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.service.command.CommandSession;
+import org.eclipse.equinox.console.completion.common.Completer;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+
+public class CompletionHandlerTests {
+
+ private static final String COMMANDS = ".commands";
+ private static final String WORK_DIR_NAME = "work";
+ private static final String TESTFILE = "testfile";
+ private static final String TESTOUTPUT = "testoutput";
+ private static final String FILE = "file";
+
+ @Before
+ public void init() throws IOException {
+ File currentDir = new File(".");
+ File[] files = currentDir.listFiles();
+ for (File file : files) {
+ if(file.getName().equals(WORK_DIR_NAME)) {
+ clean();
+ break;
+ }
+ }
+
+ File workDir = new File(currentDir.getAbsolutePath() + File.separator + WORK_DIR_NAME);
+ workDir.mkdir();
+
+ createFile(workDir, TESTFILE);
+ createFile(workDir, TESTOUTPUT);
+ createFile(workDir, FILE);
+ }
+
+ @Test
+ public void testGetCandidates() throws Exception {
+ BundleContext context = createMock(BundleContext.class);
+ expect(context.getServiceReferences(Completer.class.getName(), null)).andReturn(null).anyTimes();
+ replay(context);
+
+ Set<String> variables = new HashSet<String>();
+ variables.add("SCOPE");
+ variables.add("PROMPT");
+ variables.add("ECHO_ON");
+ variables.add("ECHO");
+
+ Set<String> commands = new HashSet<String>();
+ commands.add("equinox:bundles");
+ commands.add("equinox:diag");
+ commands.add("equinox:setprop");
+ commands.add("gogo:lb");
+ commands.add("gogo:echo");
+ commands.add("gogo:set");
+
+ CommandSession session = createMock(CommandSession.class);
+ expect(session.get(null)).andReturn(variables).anyTimes();
+ expect(session.get(COMMANDS)).andReturn(commands).anyTimes();
+ replay(session);
+
+ CompletionHandler completer = new CompletionHandler(context, session);
+ Map<String, Integer> candidates;
+
+ candidates = completer.getCandidates("$SC".getBytes(), 2);
+ assertNotNull("Candidates null", candidates);
+ assertEquals("Candidates not as expected", 1, candidates.size());
+ assertNotNull("SCOPE should be in the resultset, but it is not", candidates.get("SCOPE"));
+
+ candidates = completer.getCandidates("$EC".getBytes(), 2);
+ assertNotNull("Candidates null", candidates);
+ assertEquals("Candidates not as expected", 2, candidates.size());
+ assertNotNull("ECHO_ON should be in the resultset, but it is not", candidates.get("ECHO_ON"));
+ assertNotNull("ECHO should be in the resultset, but it is not", candidates.get("ECHO"));
+
+ candidates = completer.getCandidates("$AB".getBytes(), 2);
+ assertNotNull("Candidates null", candidates);
+ assertEquals("Candidates not as expected", 0, candidates.size());
+
+ completer = new CompletionHandler(context, session);
+ candidates = completer.getCandidates("se".getBytes(), 2);
+ assertNotNull("Candidates null", candidates);
+ assertEquals("Candidates not as expected", 2, candidates.size());
+ assertNotNull("set should be in the resultset, but it is not", candidates.get("set"));
+ assertNotNull("setprop should be in the resultset, but it is not", candidates.get("setprop"));
+
+ candidates = completer.getCandidates("equinox:bun".getBytes(), "equinox:bun".length());
+ assertNotNull("Candidates null", candidates);
+ assertEquals("Candidates not as expected", 1, candidates.size());
+ assertNotNull("equinox:bundles should be in the resultset, but it is not", candidates.get("equinox:bundles"));
+
+ candidates = completer.getCandidates("ec".getBytes(), 2);
+ assertNotNull("Candidates null", candidates);
+ assertEquals("Candidates not as expected", 1, candidates.size());
+ assertNotNull("echo should be in the resultset, but it is not", candidates.get("echo"));
+
+ candidates = completer.getCandidates("head".getBytes(), "head".length());
+ assertNotNull("Candidates null", candidates);
+ assertEquals("Candidates not as expected", 0, candidates.size());
+
+ completer = new CompletionHandler(context, session);
+ candidates = completer.getCandidates("wor".getBytes(), "wor".length());
+ assertNotNull("Candidates null", candidates);
+ assertEquals("Candidates not as expected", 1, candidates.size());
+ assertNotNull("work should be in the resultset, but it is not", candidates.get(WORK_DIR_NAME));
+
+ candidates = completer.getCandidates("work/test".getBytes(), "work/test".length());
+ assertNotNull("Candidates null", candidates);
+ assertEquals("Candidates not as expected", 2, candidates.size());
+ assertNotNull("testfile should be in the resultset, but it is not", candidates.get(TESTFILE));
+ assertNotNull("testoutput should be in the resultset, but it is not", candidates.get(TESTOUTPUT));
+
+ candidates = completer.getCandidates("work/".getBytes(), "work/".length());
+ assertEquals("Candidates not as expected", 3, candidates.size());
+ assertNotNull("testfile should be in the resultset, but it is not", candidates.get(TESTFILE));
+ assertNotNull("testoutput should be in the resultset, but it is not", candidates.get(TESTOUTPUT));
+ assertNotNull("file should be in the resultset, but it is not", candidates.get(FILE));
+ }
+
+ @After
+ public void cleanUp() {
+ clean();
+ }
+
+ private void clean() {
+ File currentFile = new File(".");
+ File workDir = new File(currentFile.getAbsolutePath() + File.separator + WORK_DIR_NAME);
+ File[] files = workDir.listFiles();
+ for (File file : files) {
+ file.delete();
+ }
+ workDir.delete();
+ }
+
+ private void createFile(File parentDir, String filename) throws IOException {
+ File file = new File(parentDir.getAbsolutePath() + File.separator + filename);
+ PrintWriter out = new PrintWriter(new FileOutputStream(file));
+ out.write(filename);
+ out.flush();
+ out.close();
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/completion/FileNamesCompleterTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/completion/FileNamesCompleterTests.java
new file mode 100755
index 000000000..91540a13f
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/completion/FileNamesCompleterTests.java
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * 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 static org.junit.Assert.*;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Map;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class FileNamesCompleterTests {
+ private static final String WORK_DIR_NAME = "work";
+ private static final String TESTFILE = "testfile";
+ private static final String TESTOUTPUT = "testoutput";
+ private static final String FILE = "file";
+
+ @Before
+ public void init() throws IOException {
+ File currentDir = new File(".");
+ File[] files = currentDir.listFiles();
+ for (File file : files) {
+ if(file.getName().equals(WORK_DIR_NAME)) {
+ clean();
+ break;
+ }
+ }
+
+ File workDir = new File(currentDir.getAbsolutePath() + File.separator + WORK_DIR_NAME);
+ workDir.mkdir();
+
+ createFile(workDir, TESTFILE);
+ createFile(workDir, TESTOUTPUT);
+ createFile(workDir, FILE);
+ }
+
+ @Test
+ public void testGetCandidates() {
+ FileNamesCompleter completer = new FileNamesCompleter();
+
+ Map<String, Integer> candidates;
+
+ candidates = completer.getCandidates("wor", "wor".length());
+ assertNotNull("Candidates null", candidates);
+ assertEquals("Candidates not as expected", 1, candidates.size());
+ assertNotNull("work should be in the resultset, but it is not", candidates.get(WORK_DIR_NAME));
+
+ candidates = completer.getCandidates("work/test", "work/test".length());
+ assertNotNull("Candidates null", candidates);
+ assertEquals("Candidates not as expected", 2, candidates.size());
+ assertNotNull("testfile should be in the resultset, but it is not", candidates.get(TESTFILE));
+ assertNotNull("testoutput should be in the resultset, but it is not", candidates.get(TESTOUTPUT));
+
+ candidates = completer.getCandidates(WORK_DIR_NAME + "/", 5);
+ assertEquals("Candidates not as expected", 3, candidates.size());
+ assertNotNull("testfile should be in the resultset, but it is not", candidates.get(TESTFILE));
+ assertNotNull("testoutput should be in the resultset, but it is not", candidates.get(TESTOUTPUT));
+ assertNotNull("file should be in the resultset, but it is not", candidates.get(FILE));
+ }
+
+ @After
+ public void cleanUp() {
+ clean();
+ }
+
+ private void clean() {
+ File currentFile = new File(".");
+ File workDir = new File(currentFile.getAbsolutePath() + File.separator + WORK_DIR_NAME);
+ File[] files = workDir.listFiles();
+ for (File file : files) {
+ file.delete();
+ }
+ workDir.delete();
+ }
+
+ private void createFile(File parentDir, String filename) throws IOException {
+ File file = new File(parentDir.getAbsolutePath() + File.separator + filename);
+ PrintWriter out = new PrintWriter(new FileOutputStream(file));
+ out.write(filename);
+ out.flush();
+ out.close();
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/completion/StringsCompleterTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/completion/StringsCompleterTests.java
new file mode 100755
index 000000000..05096ba5b
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/completion/StringsCompleterTests.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 static org.junit.Assert.*;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.Test;
+
+public class StringsCompleterTests {
+
+ @Test
+ public void testGetCandidates() {
+ Set<String> strings = new HashSet<String>();
+ strings.add("command");
+ strings.add("SCOPE");
+ strings.add("equinox:bundles");
+ strings.add("common");
+
+ StringsCompleter completer = new StringsCompleter(strings, false);
+ Map<String, Integer> candidates;
+
+ candidates = completer.getCandidates("sco", 3);
+ assertNotNull("Candidates null", candidates);
+ assertEquals("Candidates not as expected", 1, candidates.size());
+ assertNotNull("SCOPE should be in the resultset, but it is not", candidates.get("SCOPE"));
+
+ candidates = completer.getCandidates("com", 3);
+ assertNotNull("Candidates null", candidates);
+ assertEquals("Candidates not as expected", 2, candidates.size());
+ assertNotNull("command should be in the resultset, but it is not", candidates.get("command"));
+ assertNotNull("common should be in the resultset, but it is not", candidates.get("common"));
+
+ candidates = completer.getCandidates("tr", 2);
+ assertNotNull("Candidates null", candidates);
+ assertEquals("Candidates not as expected", 0, candidates.size());
+
+ completer = new StringsCompleter(strings, true);
+
+ candidates = completer.getCandidates("sco", 3);
+ assertNotNull("Candidates null", candidates);
+ assertEquals("Candidates not as expected", 0, candidates.size());
+
+ candidates = completer.getCandidates("SCO", 3);
+ assertNotNull("Candidates null", candidates);
+ assertEquals("Candidates not as expected", 1, candidates.size());
+ assertNotNull("SCOPE should be in the resultset, but it is not", candidates.get("SCOPE"));
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/completion/VariableNamesCompleterTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/completion/VariableNamesCompleterTests.java
new file mode 100755
index 000000000..2b6ca5290
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/completion/VariableNamesCompleterTests.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * 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 static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.service.command.CommandSession;
+import org.junit.Test;
+
+public class VariableNamesCompleterTests {
+
+ @Test
+ public void testGetCandidates() {
+ Set<String> variables = new HashSet<String>();
+ variables.add("SCOPE");
+ variables.add("PROMPT");
+ variables.add("ECHO_ON");
+ variables.add("ECHO");
+
+ CommandSession session = createMock(CommandSession.class);
+ expect(session.get(null)).andReturn(variables).times(3);
+ replay(session);
+
+ VariableNamesCompleter completer = new VariableNamesCompleter(session);
+ Map<String, Integer> candidates;
+
+ candidates = completer.getCandidates("SC", 2);
+ assertNotNull("Candidates null", candidates);
+ assertEquals("Candidates not as expected", 1, candidates.size());
+ assertNotNull("SCOPE should be in the resultset, but it is not", candidates.get("SCOPE"));
+
+ candidates = completer.getCandidates("EC", 2);
+ assertNotNull("Candidates null", candidates);
+ assertEquals("Candidates not as expected", 2, candidates.size());
+ assertNotNull("ECHO_ON should be in the resultset, but it is not", candidates.get("ECHO_ON"));
+ assertNotNull("ECHO should be in the resultset, but it is not", candidates.get("ECHO"));
+
+ candidates = completer.getCandidates("AB", 2);
+ assertNotNull("Candidates null", candidates);
+ assertEquals("Candidates not as expected", 0, candidates.size());
+
+ verify(session);
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/jaas/RolePrincipalTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/jaas/RolePrincipalTests.java
new file mode 100755
index 000000000..edae26f3b
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/jaas/RolePrincipalTests.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * 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 static org.junit.Assert.*;
+
+import org.junit.Test;
+
+public class RolePrincipalTests {
+
+ private static final String ROLE_NAME = "administrator";
+
+ @Test
+ public void testHashCode() {
+ RolePrincipal role = new RolePrincipal(ROLE_NAME);
+ assertEquals("Role hash code not as expected", 73 + ROLE_NAME.hashCode(), role.hashCode());
+ }
+
+ @Test
+ public void testGetName() {
+ RolePrincipal role = new RolePrincipal(ROLE_NAME);
+ assertEquals("Role not as expected", ROLE_NAME, role.getName());
+ }
+
+ @Test
+ public void testEqualsObject() {
+ RolePrincipal role = new RolePrincipal(ROLE_NAME);
+ RolePrincipal sameRole = new RolePrincipal(ROLE_NAME);
+ RolePrincipal emptyRole = new RolePrincipal(null);
+
+ assertTrue("Roles should be equal", role.equals(role));
+ assertTrue("Roles should be equal", role.equals(sameRole));
+ assertFalse("Roles should not be equal", role.equals(emptyRole));
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/jaas/UserPrincipalTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/jaas/UserPrincipalTests.java
new file mode 100755
index 000000000..50239be1c
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/jaas/UserPrincipalTests.java
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * 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 static org.junit.Assert.*;
+
+import java.util.Set;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class UserPrincipalTests {
+
+ private static final String USERNAME = "username";
+ private static final String PASSWORD = "password";
+ private static final String OTHERUSER = "otheruser";
+ private static final String OTHERPASSWORD = "otherpass";
+ private static final String WRONG_PASS = "wrong_pass";
+ private static final String ROLE = "administrator";
+ private static final String OTHERROLE = "otherrole";
+
+ @Test
+ public void testHashCode() {
+ UserPrincipal user = new UserPrincipal(USERNAME, PASSWORD);
+ RolePrincipal role = new RolePrincipal(ROLE);
+ user.addRole(role);
+
+ int expectedHash = 73 + USERNAME.hashCode();
+ expectedHash = 73*expectedHash + PASSWORD.hashCode();
+ expectedHash = 73*expectedHash + role.hashCode();
+ assertEquals("User hash code not as expected", expectedHash, user.hashCode());
+ }
+
+ @Test
+ public void testGetName() {
+ UserPrincipal user = new UserPrincipal(USERNAME, PASSWORD);
+ assertEquals("Username not as expected", USERNAME, user.getName());
+ }
+
+ @Test
+ public void testAuthenticate() {
+ UserPrincipal user = new UserPrincipal(USERNAME, PASSWORD);
+ assertTrue("User should be successfully authenticated", user.authenticate(PASSWORD.toCharArray()));
+ assertFalse("User should not be authenticated", user.authenticate(WRONG_PASS.toCharArray()));
+ }
+
+ @Test
+ public void testGetRoles() {
+ UserPrincipal user = new UserPrincipal(USERNAME, PASSWORD);
+ RolePrincipal role = new RolePrincipal(ROLE);
+ user.addRole(role);
+ Set<RolePrincipal> roles = user.getRoles();
+ assertEquals("There should be one role", 1, roles.size());
+ assertTrue("User roles should contain the role administrator", roles.contains(role));
+ }
+
+ @Test
+ public void testEqualsObject() {
+ UserPrincipal user = new UserPrincipal(USERNAME, PASSWORD);
+ RolePrincipal role = new RolePrincipal(ROLE);
+ user.addRole(role);
+
+ UserPrincipal sameUser = new UserPrincipal(USERNAME, PASSWORD);
+ RolePrincipal sameRole = new RolePrincipal(ROLE);
+ sameUser.addRole(sameRole);
+
+ UserPrincipal otherUser = new UserPrincipal(OTHERUSER, OTHERPASSWORD);
+ RolePrincipal otherRole = new RolePrincipal(OTHERROLE);
+ otherUser.addRole(otherRole);
+
+ UserPrincipal userOtherRole = new UserPrincipal(USERNAME, PASSWORD);
+ RolePrincipal otherRolePrincipal = new RolePrincipal(OTHERROLE);
+ userOtherRole.addRole(otherRolePrincipal);
+
+ assertTrue("User should be equal to itself", user.equals(user));
+ assertTrue("Users should be equal", user.equals(sameUser));
+ assertFalse("Users should not be equal", user.equals(otherUser));
+ assertFalse("Users should not be equal", user.equals(userOtherRole));
+ }
+
+ @Test
+ public void testDestroy() {
+ UserPrincipal user = new UserPrincipal(USERNAME, PASSWORD);
+ UserPrincipal same = new UserPrincipal(USERNAME, PASSWORD);
+
+ user.destroy();
+ assertFalse("Users should not be equal", user.equals(same));
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/ssh/SshCommandTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/ssh/SshCommandTests.java
new file mode 100755
index 000000000..7fab9788b
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/ssh/SshCommandTests.java
@@ -0,0 +1,188 @@
+/*******************************************************************************
+ * 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.ByteArrayOutputStream;
+import java.io.File;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringBufferInputStream;
+import java.util.Dictionary;
+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.ClientChannel;
+import org.apache.sshd.ClientSession;
+import org.apache.sshd.SshClient;
+import org.apache.sshd.client.future.ConnectFuture;
+import org.apache.sshd.client.future.DefaultConnectFuture;
+import org.apache.sshd.server.Environment;
+import org.easymock.EasyMock;
+import org.eclipse.equinox.console.common.ConsoleInputStream;
+import org.eclipse.equinox.console.storage.DigestUtil;
+import org.eclipse.equinox.console.storage.SecureUserStore;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+
+
+public class SshCommandTests {
+ private static final int TEST_CONTENT = 100;
+ private static final String USER_STORE_FILE_NAME = "org.eclipse.equinox.console.jaas.file";
+ private static final String JAAS_CONFIG_FILE_NAME = "jaas.config";
+ private static final String JAAS_CONFIG_PROPERTY_NAME = "java.security.auth.login.config";
+ private static final String DEFAULT_USER_STORAGE = "osgi.console.ssh.useDefaultSecureStorage";
+ private static final String SSH_PORT_PROP_NAME = "osgi.console.ssh";
+ private static final String USE_CONFIG_ADMIN_PROP = "osgi.console.useConfigAdmin";
+ private static final String STORE_FILE_NAME = SshCommandTests.class.getName() + "_store";
+ private static final String GOGO_SHELL_COMMAND = "gosh --login --noshutdown";
+ private static final String TRUE = "true";
+ private static final String FALSE = "false";
+ private static final String USERNAME = "username";
+ private static final String PASSWORD = "password";
+ private static final String START_COMMAND = "start";
+ private static final String STOP_COMMAND = "stop";
+ private static final String TERM_PROPERTY = "TERM";
+ private static final String XTERM = "XTERM";
+ private static final String HOST = "localhost";
+ private static final int SSH_PORT = 2222;
+ private static final long WAIT_TIME = 5000;
+
+ @Before
+ public void init() throws Exception {
+ clean();
+ initStore();
+ initJaasConfigFile();
+ }
+
+ @Test
+ public void testSshCommand() throws Exception {
+ CommandSession session = EasyMock.createMock(CommandSession.class);
+ EasyMock.makeThreadSafe(session, true);
+ session.put((String)EasyMock.anyObject(), EasyMock.anyObject());
+ EasyMock.expectLastCall().times(5);
+ EasyMock.expect(session.execute(GOGO_SHELL_COMMAND)).andReturn(null);
+ session.close();
+ EasyMock.expectLastCall();
+ EasyMock.replay(session);
+
+ CommandProcessor processor = EasyMock.createMock(CommandProcessor.class);
+ EasyMock.expect(processor.createSession((ConsoleInputStream)EasyMock.anyObject(), (PrintStream)EasyMock.anyObject(), (PrintStream)EasyMock.anyObject())).andReturn(session);
+ EasyMock.replay(processor);
+
+ BundleContext context = EasyMock.createMock(BundleContext.class);
+ EasyMock.makeThreadSafe(context, true);
+ EasyMock.expect(context.getProperty(USE_CONFIG_ADMIN_PROP)).andReturn(FALSE);
+ EasyMock.expect(context.getProperty(DEFAULT_USER_STORAGE)).andReturn(TRUE).anyTimes();
+ EasyMock.expect(context.getProperty(SSH_PORT_PROP_NAME)).andReturn(Integer.toString(SSH_PORT));
+ EasyMock.expect(context.registerService((String)EasyMock.anyObject(), EasyMock.anyObject(), (Dictionary<String, ?>)EasyMock.anyObject())).andReturn(null);
+ EasyMock.replay(context);
+
+ Map<String, String> environment = new HashMap<String, String>();
+ environment.put(TERM_PROPERTY, XTERM);
+ Environment env = EasyMock.createMock(Environment.class);
+ EasyMock.expect(env.getEnv()).andReturn(environment);
+ EasyMock.replay(env);
+
+ SshCommand command = new SshCommand(processor, context);
+ command.ssh(new String[] {START_COMMAND});
+
+ SshClient client = SshClient.setUpDefaultClient();
+ client.start();
+ try {
+ ConnectFuture connectFuture = client.connect(HOST, SSH_PORT);
+ DefaultConnectFuture defaultConnectFuture = (DefaultConnectFuture) connectFuture;
+
+ try {
+ Thread.sleep(WAIT_TIME);
+ } catch (InterruptedException ie) {
+ // do nothing
+ }
+ ClientSession sshSession = defaultConnectFuture.getSession();
+
+ int ret = ClientSession.WAIT_AUTH;
+ sshSession.authPassword(USERNAME, PASSWORD);
+ ret = sshSession.waitFor(ClientSession.WAIT_AUTH | ClientSession.CLOSED | ClientSession.AUTHED, 0);
+
+ if ((ret & ClientSession.CLOSED) != 0) {
+ System.err.println("error");
+ System.exit(-1);
+ }
+ ClientChannel channel = sshSession.createChannel("shell");
+ channel.setIn(new StringBufferInputStream(TEST_CONTENT + "\n"));
+ ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+ channel.setOut(byteOut);
+ channel.setErr(byteOut);
+ channel.open();
+ try {
+ Thread.sleep(WAIT_TIME);
+ } catch (InterruptedException ie) {
+ // do nothing
+ }
+ byte[] output = byteOut.toByteArray();
+ Assert.assertEquals("Output not as expected",Integer.toString(TEST_CONTENT), new String(output).trim());
+ sshSession.close(true);
+ } finally {
+ client.stop();
+ }
+
+ command.ssh(new String[] {STOP_COMMAND});
+ return;
+ }
+
+ @After
+ public void cleanUp() {
+ clean();
+ }
+
+ private void clean() {
+ System.setProperty(USER_STORE_FILE_NAME, "");
+ File file = new File(STORE_FILE_NAME);
+ if (file.exists()) {
+ file.delete();
+ }
+
+ System.setProperty(JAAS_CONFIG_PROPERTY_NAME, "");
+ File jaasConfFile = new File(JAAS_CONFIG_FILE_NAME);
+ if (jaasConfFile.exists()) {
+ jaasConfFile.delete();
+ }
+ }
+
+ private void initStore() throws Exception {
+ System.setProperty(USER_STORE_FILE_NAME, STORE_FILE_NAME);
+ SecureUserStore.initStorage();
+ SecureUserStore.putUser(USERNAME, DigestUtil.encrypt(PASSWORD), null);
+ }
+
+ private void initJaasConfigFile() throws Exception {
+ System.setProperty(JAAS_CONFIG_PROPERTY_NAME, JAAS_CONFIG_FILE_NAME);
+ File jaasConfFile = new File(JAAS_CONFIG_FILE_NAME);
+ if (!jaasConfFile.exists()) {
+ PrintWriter out = null;
+ try {
+ out = new PrintWriter(jaasConfFile);
+ out.println("equinox_console {");
+ out.println(" org.eclipse.equinox.console.jaas.SecureStorageLoginModule REQUIRED;");
+ out.println("};");
+ } finally {
+ if (out != null) {
+ out.close();
+ }
+ }
+ }
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/ssh/SshCommandWithConfigAdminTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/ssh/SshCommandWithConfigAdminTests.java
new file mode 100755
index 000000000..099442d77
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/ssh/SshCommandWithConfigAdminTests.java
@@ -0,0 +1,310 @@
+package org.eclipse.equinox.console.ssh;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringBufferInputStream;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.sshd.ClientChannel;
+import org.apache.sshd.ClientSession;
+import org.apache.sshd.SshClient;
+import org.apache.sshd.client.future.ConnectFuture;
+import org.apache.sshd.client.future.DefaultConnectFuture;
+import org.apache.sshd.common.RuntimeSshException;
+import org.apache.sshd.server.Environment;
+import org.easymock.EasyMock;
+import org.easymock.IAnswer;
+import org.eclipse.equinox.console.common.ConsoleInputStream;
+import org.eclipse.equinox.console.storage.DigestUtil;
+import org.eclipse.equinox.console.storage.SecureUserStore;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.BundleListener;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.cm.ManagedService;
+
+
+public class SshCommandWithConfigAdminTests {
+ private static final int TEST_CONTENT = 100;
+ private static final String USER_STORE_FILE_NAME = "org.eclipse.equinox.console.jaas.file";
+ private static final String JAAS_CONFIG_FILE_NAME = "jaas.config";
+ private static final String JAAS_CONFIG_PROPERTY_NAME = "java.security.auth.login.config";
+ private static final String DEFAULT_USER_STORAGE = "osgi.console.ssh.useDefaultSecureStorage";
+ private static final String STORE_FILE_NAME = SshCommandTests.class.getName() + "_store";
+ private static final String GOGO_SHELL_COMMAND = "gosh --login --noshutdown";
+ private static final String TRUE = "true";
+ private static final String FALSE = "false";
+ private static final String USERNAME = "username";
+ private static final String PASSWORD = "password";
+ private static final String STOP_COMMAND = "stop";
+ private static final String TERM_PROPERTY = "TERM";
+ private static final String XTERM = "XTERM";
+ private static final String HOST = "localhost";
+ private static final String SSH_PORT = "2222";
+ private static final long WAIT_TIME = 5000;
+ private static final String USE_CONFIG_ADMIN_PROP = "osgi.console.useConfigAdmin";
+ private ManagedService configurator;
+
+ @Before
+ public void init() throws Exception {
+ clean();
+ initStore();
+ initJaasConfigFile();
+ }
+
+ @Test
+ public void testSshCommandWithConfigAdmin() throws Exception {
+
+ CommandSession session = EasyMock.createMock(CommandSession.class);
+ EasyMock.makeThreadSafe(session, true);
+ session.put((String)EasyMock.anyObject(), EasyMock.anyObject());
+ EasyMock.expectLastCall().times(5);
+ EasyMock.expect(session.execute(GOGO_SHELL_COMMAND)).andReturn(null);
+ session.close();
+ EasyMock.expectLastCall();
+ EasyMock.replay(session);
+
+ CommandProcessor processor = EasyMock.createMock(CommandProcessor.class);
+ EasyMock.expect(processor.createSession((ConsoleInputStream)EasyMock.anyObject(), (PrintStream)EasyMock.anyObject(), (PrintStream)EasyMock.anyObject())).andReturn(session);
+ EasyMock.replay(processor);
+
+ final ServiceRegistration<?> registration = EasyMock.createMock(ServiceRegistration.class);
+ registration.setProperties((Dictionary)EasyMock.anyObject());
+ EasyMock.expectLastCall();
+ EasyMock.replay(registration);
+
+ BundleContext context = EasyMock.createMock(BundleContext.class);
+ EasyMock.makeThreadSafe(context, true);
+ EasyMock.expect(context.getProperty(USE_CONFIG_ADMIN_PROP)).andReturn(TRUE);
+ EasyMock.expect(context.getProperty(DEFAULT_USER_STORAGE)).andReturn(TRUE).anyTimes();
+ EasyMock.expect(
+ (ServiceRegistration) context.registerService(
+ (String)EasyMock.anyObject(),
+ (ManagedService)EasyMock.anyObject(),
+ (Dictionary<String, ?>)EasyMock.anyObject())
+ ).andAnswer((IAnswer<ServiceRegistration<?>>) new IAnswer<ServiceRegistration<?>>() {
+ public ServiceRegistration<?> answer() {
+ configurator = (ManagedService) EasyMock.getCurrentArguments()[1];
+ return registration;
+ }
+ });
+ EasyMock.expect(
+ context.registerService(
+ (String)EasyMock.anyObject(),
+ (SshCommand)EasyMock.anyObject(),
+ (Dictionary<String, ?>)EasyMock.anyObject())).andReturn(null);
+ EasyMock.replay(context);
+
+ Map<String, String> environment = new HashMap<String, String>();
+ environment.put(TERM_PROPERTY, XTERM);
+ Environment env = EasyMock.createMock(Environment.class);
+ EasyMock.expect(env.getEnv()).andReturn(environment);
+ EasyMock.replay(env);
+
+ SshCommand command = new SshCommand(processor, context);
+ Dictionary props = new Hashtable();
+ props.put("port", SSH_PORT);
+ props.put("host", HOST);
+ props.put("enabled", TRUE);
+ configurator.updated(props);
+
+ SshClient client = SshClient.setUpDefaultClient();
+ client.start();
+ try {
+ ConnectFuture connectFuture = client.connect(HOST, Integer.valueOf(SSH_PORT));
+ DefaultConnectFuture defaultConnectFuture = (DefaultConnectFuture) connectFuture;
+
+ try {
+ Thread.sleep(WAIT_TIME);
+ } catch (InterruptedException ie) {
+ // do nothing
+ }
+ ClientSession sshSession = defaultConnectFuture.getSession();
+
+ int ret = ClientSession.WAIT_AUTH;
+ sshSession.authPassword(USERNAME, PASSWORD);
+ ret = sshSession.waitFor(ClientSession.WAIT_AUTH | ClientSession.CLOSED | ClientSession.AUTHED, 0);
+
+ if ((ret & ClientSession.CLOSED) != 0) {
+ System.err.println("error");
+ System.exit(-1);
+ }
+ ClientChannel channel = sshSession.createChannel("shell");
+ channel.setIn(new StringBufferInputStream(TEST_CONTENT + "\n"));
+ ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+ channel.setOut(byteOut);
+ channel.setErr(byteOut);
+ channel.open();
+ try {
+ Thread.sleep(WAIT_TIME);
+ } catch (InterruptedException ie) {
+ // do nothing
+ }
+ byte[] output = byteOut.toByteArray();
+ Assert.assertEquals("Output not as expected",Integer.toString(TEST_CONTENT), new String(output).trim());
+ sshSession.close(true);
+ } finally {
+ client.stop();
+ }
+
+ command.ssh(new String[] {STOP_COMMAND});
+ return;
+ }
+
+ @Test
+ public void testSshCommandWithConfigAdminDisabledSsh() throws Exception {
+ testDisabled(false);
+ }
+
+ @Test
+ public void testSshCommandWithConfigAdminDisabledSshByDefault() throws Exception {
+ testDisabled(true);
+ }
+
+ private void testDisabled(boolean isDefault) throws Exception {
+ CommandSession session = EasyMock.createMock(CommandSession.class);
+ session.put((String)EasyMock.anyObject(), EasyMock.anyObject());
+ EasyMock.expectLastCall().times(4);
+ EasyMock.expect(session.execute(GOGO_SHELL_COMMAND)).andReturn(null);
+ session.close();
+ EasyMock.expectLastCall();
+ EasyMock.replay(session);
+
+ CommandProcessor processor = EasyMock.createMock(CommandProcessor.class);
+ EasyMock.expect(processor.createSession((ConsoleInputStream)EasyMock.anyObject(), (PrintStream)EasyMock.anyObject(), (PrintStream)EasyMock.anyObject())).andReturn(session);
+ EasyMock.replay(processor);
+
+ final ServiceRegistration<?> registration = EasyMock.createMock(ServiceRegistration.class);
+ registration.setProperties((Dictionary)EasyMock.anyObject());
+ EasyMock.expectLastCall();
+ EasyMock.replay(registration);
+
+ BundleContext context = EasyMock.createMock(BundleContext.class);
+ EasyMock.expect(context.getProperty(USE_CONFIG_ADMIN_PROP)).andReturn(TRUE);
+ EasyMock.expect(context.getProperty(DEFAULT_USER_STORAGE)).andReturn(TRUE).anyTimes();
+ EasyMock.expect(
+ (ServiceRegistration) context.registerService(
+ (String)EasyMock.anyObject(),
+ (ManagedService)EasyMock.anyObject(),
+ (Dictionary<String, ?>)EasyMock.anyObject())
+ ).andAnswer((IAnswer<ServiceRegistration<?>>) new IAnswer<ServiceRegistration<?>>() {
+ public ServiceRegistration<?> answer() {
+ configurator = (ManagedService) EasyMock.getCurrentArguments()[1];
+ return registration;
+ }
+ });
+ EasyMock.expect(
+ context.registerService(
+ (String)EasyMock.anyObject(),
+ (SshCommand)EasyMock.anyObject(),
+ (Dictionary<String, ?>)EasyMock.anyObject())).andReturn(null);
+ EasyMock.replay(context);
+
+ Map<String, String> environment = new HashMap<String, String>();
+ environment.put(TERM_PROPERTY, XTERM);
+ Environment env = EasyMock.createMock(Environment.class);
+ EasyMock.expect(env.getEnv()).andReturn(environment);
+ EasyMock.replay(env);
+
+ SshCommand command = new SshCommand(processor, context);
+ Dictionary props = new Hashtable();
+ props.put("port", SSH_PORT);
+ props.put("host", HOST);
+ if (isDefault == false) {
+ props.put("enabled", FALSE);
+ }
+ configurator.updated(props);
+
+ SshClient client = SshClient.setUpDefaultClient();
+ client.start();
+ try {
+ ConnectFuture connectFuture = client.connect(HOST, Integer.valueOf(SSH_PORT));
+ DefaultConnectFuture defaultConnectFuture = (DefaultConnectFuture) connectFuture;
+
+ try {
+ Thread.sleep(WAIT_TIME);
+ } catch (InterruptedException ie) {
+ // do nothing
+ }
+ ClientSession sshSession;
+ try {
+ sshSession = defaultConnectFuture.getSession();
+ Assert.fail("It should not be possible to connect to " + HOST + ":" + SSH_PORT);
+ } catch (RuntimeSshException e) {
+ //this is expected
+ }
+ } finally {
+ client.stop();
+ }
+
+ try {
+ command.ssh(new String[] {STOP_COMMAND});
+ } catch (IllegalStateException e) {
+ // this is expected
+ }
+ return;
+ }
+
+ @After
+ public void cleanUp() {
+ clean();
+ }
+
+ private void clean() {
+ System.setProperty(USER_STORE_FILE_NAME, "");
+ File file = new File(STORE_FILE_NAME);
+ if (file.exists()) {
+ file.delete();
+ }
+
+ System.setProperty(JAAS_CONFIG_PROPERTY_NAME, "");
+ File jaasConfFile = new File(JAAS_CONFIG_FILE_NAME);
+ if (jaasConfFile.exists()) {
+ jaasConfFile.delete();
+ }
+ }
+
+ private void initStore() throws Exception {
+ System.setProperty(USER_STORE_FILE_NAME, STORE_FILE_NAME);
+ SecureUserStore.initStorage();
+ SecureUserStore.putUser(USERNAME, DigestUtil.encrypt(PASSWORD), null);
+ }
+
+ private void initJaasConfigFile() throws Exception {
+ System.setProperty(JAAS_CONFIG_PROPERTY_NAME, JAAS_CONFIG_FILE_NAME);
+ File jaasConfFile = new File(JAAS_CONFIG_FILE_NAME);
+ if (!jaasConfFile.exists()) {
+ PrintWriter out = null;
+ try {
+ out = new PrintWriter(jaasConfFile);
+ out.println("equinox_console {");
+ out.println(" org.eclipse.equinox.console.jaas.SecureStorageLoginModule REQUIRED;");
+ out.println("};");
+ } finally {
+ if (out != null) {
+ out.close();
+ }
+ }
+ }
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/ssh/SshDisconnectCommandTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/ssh/SshDisconnectCommandTests.java
new file mode 100755
index 000000000..8ab10b244
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/ssh/SshDisconnectCommandTests.java
@@ -0,0 +1,234 @@
+package org.eclipse.equinox.console.ssh;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringBufferInputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.Dictionary;
+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.ClientChannel;
+import org.apache.sshd.ClientSession;
+import org.apache.sshd.SshClient;
+import org.apache.sshd.client.future.ConnectFuture;
+import org.apache.sshd.client.future.DefaultConnectFuture;
+import org.apache.sshd.server.Environment;
+import org.easymock.EasyMock;
+import org.easymock.IAnswer;
+import org.eclipse.equinox.console.commands.DisconnectCommand;
+import org.eclipse.equinox.console.common.ConsoleInputStream;
+import org.eclipse.equinox.console.storage.DigestUtil;
+import org.eclipse.equinox.console.storage.SecureUserStore;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+
+public class SshDisconnectCommandTests {
+ private static final int TEST_CONTENT = 100;
+ private static final String USER_STORE_FILE_NAME = "org.eclipse.equinox.console.jaas.file";
+ private static final String JAAS_CONFIG_FILE_NAME = "jaas.config";
+ private static final String JAAS_CONFIG_PROPERTY_NAME = "java.security.auth.login.config";
+ private static final String DEFAULT_USER_STORAGE = "osgi.console.ssh.useDefaultSecureStorage";
+ private static final String USE_CONFIG_ADMIN_PROP = "osgi.console.useConfigAdmin";
+ private static final String SSH_PORT_PROP_NAME = "osgi.console.ssh";
+ private static final String STORE_FILE_NAME = SshCommandTests.class.getName() + "_store";
+ private static final String GOGO_SHELL_COMMAND = "gosh --login --noshutdown";
+ private static final String TRUE = "true";
+ private static final String FALSE = "false";
+ private static final String USERNAME = "username";
+ private static final String PASSWORD = "password";
+ private static final String START_COMMAND = "start";
+ private static final String STOP_COMMAND = "stop";
+ private static final String TERM_PROPERTY = "TERM";
+ private static final String XTERM = "XTERM";
+ private static final String HOST = "localhost";
+ private static final int SSH_PORT = 2222;
+ private static final long WAIT_TIME = 5000;
+ private SshSession sshSession;
+ private InputStream in;
+
+ @Before
+ public void init() throws Exception {
+ clean();
+ initStore();
+ initJaasConfigFile();
+ }
+
+ @Test
+ public void testSshCommand() throws Exception {
+ final CommandSession session = EasyMock.createMock(CommandSession.class);
+ EasyMock.makeThreadSafe(session, true);
+ session.put((String)EasyMock.anyObject(), EasyMock.anyObject());
+ EasyMock.expectLastCall();
+ session.put((String)EasyMock.anyObject(), EasyMock.anyObject());
+ EasyMock.expectLastCall();
+ session.put((String)EasyMock.anyObject(), EasyMock.anyObject());
+ EasyMock.expectLastCall();
+ session.put((String)EasyMock.anyObject(), EasyMock.anyObject());
+ EasyMock.expectLastCall();
+ session.put((String)EasyMock.anyObject(), EasyMock.anyObject());
+ EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
+
+ @Override
+ public Object answer() throws Throwable {
+ sshSession = (SshSession)EasyMock.getCurrentArguments()[1];
+ return null;
+ }
+
+ });
+ EasyMock.expect(session.execute(GOGO_SHELL_COMMAND)).andReturn(null);
+ EasyMock.expect(session.get("CLOSEABLE")).andReturn(sshSession);
+ session.close();
+ EasyMock.expectLastCall().atLeastOnce();
+ EasyMock.replay(session);
+
+ CommandProcessor processor = EasyMock.createMock(CommandProcessor.class);
+ EasyMock.expect(processor.createSession((ConsoleInputStream)EasyMock.anyObject(), (PrintStream)EasyMock.anyObject(), (PrintStream)EasyMock.anyObject())).andReturn(session);
+ EasyMock.replay(processor);
+
+ BundleContext context = EasyMock.createMock(BundleContext.class);
+ EasyMock.makeThreadSafe(context, true);
+ EasyMock.expect(context.getProperty(USE_CONFIG_ADMIN_PROP)).andReturn(FALSE);
+ EasyMock.expect(context.getProperty(DEFAULT_USER_STORAGE)).andReturn(TRUE).anyTimes();
+ EasyMock.expect(context.getProperty(SSH_PORT_PROP_NAME)).andReturn(Integer.toString(SSH_PORT));
+ EasyMock.expect(context.registerService((String)EasyMock.anyObject(), EasyMock.anyObject(), (Dictionary<String, ?>)EasyMock.anyObject())).andReturn(null);
+ EasyMock.replay(context);
+
+ Map<String, String> environment = new HashMap<String, String>();
+ environment.put(TERM_PROPERTY, XTERM);
+ Environment env = EasyMock.createMock(Environment.class);
+ EasyMock.expect(env.getEnv()).andReturn(environment);
+ EasyMock.replay(env);
+
+ SshCommand command = new SshCommand(processor, context);
+ command.ssh(new String[] {START_COMMAND});
+
+ SshClient client = SshClient.setUpDefaultClient();
+ client.start();
+ try {
+ ConnectFuture connectFuture = client.connect(HOST, SSH_PORT);
+ DefaultConnectFuture defaultConnectFuture = (DefaultConnectFuture) connectFuture;
+
+ try {
+ Thread.sleep(WAIT_TIME);
+ } catch (InterruptedException ie) {
+ // do nothing
+ }
+ ClientSession sshSession = defaultConnectFuture.getSession();
+
+ int ret = ClientSession.WAIT_AUTH;
+ sshSession.authPassword(USERNAME, PASSWORD);
+ ret = sshSession.waitFor(ClientSession.WAIT_AUTH | ClientSession.CLOSED | ClientSession.AUTHED, 0);
+
+ if ((ret & ClientSession.CLOSED) != 0) {
+ System.err.println("error");
+ System.exit(-1);
+ }
+ ClientChannel channel = sshSession.createChannel("shell");
+
+ PipedOutputStream outputStream = new PipedOutputStream();
+ PipedInputStream inputStream = new PipedInputStream(outputStream);
+
+ final DisconnectCommand disconnectCommand = new DisconnectCommand(context);
+ in = System.in;
+ System.setIn(inputStream);
+
+ ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+ channel.setIn(new StringBufferInputStream(TEST_CONTENT + "\n"));
+ channel.setOut(byteOut);
+ channel.setErr(byteOut);
+ channel.open();
+
+
+ new Thread() {
+ public void run() {
+ disconnectCommand.disconnect(session);
+ }
+ }.start();
+
+ outputStream.write(new byte[]{'y'});
+ outputStream.write('\n');
+ outputStream.flush();
+
+ Thread.sleep(WAIT_TIME);
+
+ try {
+ Thread.sleep(WAIT_TIME);
+ } catch (InterruptedException ie) {
+ // do nothing
+ }
+
+ try {
+ outputStream.write(TEST_CONTENT);
+ outputStream.write('\n');
+ outputStream.flush();
+ Assert.fail("Connection not closed");
+ } catch (Exception e) {
+ // we should be here
+ }
+ sshSession.close(true);
+ } finally {
+ client.stop();
+ }
+
+ command.ssh(new String[] {STOP_COMMAND});
+ return;
+ }
+
+ @After
+ public void cleanUp() {
+ clean();
+ }
+
+ private void clean() {
+ System.setProperty(USER_STORE_FILE_NAME, "");
+ File file = new File(STORE_FILE_NAME);
+ if (file.exists()) {
+ file.delete();
+ }
+
+ System.setProperty(JAAS_CONFIG_PROPERTY_NAME, "");
+ File jaasConfFile = new File(JAAS_CONFIG_FILE_NAME);
+ if (jaasConfFile.exists()) {
+ jaasConfFile.delete();
+ }
+
+ System.setIn(in);
+ }
+
+ private void initStore() throws Exception {
+ System.setProperty(USER_STORE_FILE_NAME, STORE_FILE_NAME);
+ SecureUserStore.initStorage();
+ SecureUserStore.putUser(USERNAME, DigestUtil.encrypt(PASSWORD), null);
+ }
+
+ private void initJaasConfigFile() throws Exception {
+ System.setProperty(JAAS_CONFIG_PROPERTY_NAME, JAAS_CONFIG_FILE_NAME);
+ File jaasConfFile = new File(JAAS_CONFIG_FILE_NAME);
+ if (!jaasConfFile.exists()) {
+ PrintWriter out = null;
+ try {
+ out = new PrintWriter(jaasConfFile);
+ out.println("equinox_console {");
+ out.println(" org.eclipse.equinox.console.jaas.SecureStorageLoginModule REQUIRED;");
+ out.println("};");
+ } finally {
+ if (out != null) {
+ out.close();
+ }
+ }
+ }
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/ssh/SshInputHandlerTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/ssh/SshInputHandlerTests.java
new file mode 100755
index 000000000..e7ed407a0
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/ssh/SshInputHandlerTests.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * 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.ByteArrayOutputStream;
+import java.io.StringBufferInputStream;
+
+import org.eclipse.equinox.console.common.ConsoleInputStream;
+import org.eclipse.equinox.console.common.ConsoleOutputStream;
+import org.eclipse.equinox.console.ssh.SshInputHandler;
+import org.junit.Assert;
+import org.junit.Test;
+
+
+public class SshInputHandlerTests {
+
+ private static final long WAIT_TIME = 10000;
+
+ @Test
+ public void testHandler() throws Exception {
+ StringBufferInputStream input = new StringBufferInputStream("abcde");
+ ConsoleInputStream in = new ConsoleInputStream();
+ ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+ ConsoleOutputStream out = new ConsoleOutputStream(byteOut);
+ SshInputHandler handler = new SshInputHandler(input, in, out);
+ handler.start();
+
+ // wait for the accept thread to start execution
+ try {
+ Thread.sleep(WAIT_TIME);
+ } catch (InterruptedException ie) {
+ // do nothing
+ }
+
+ String res = byteOut.toString();
+ Assert.assertTrue("Wrong input. Expected abcde, read " + res, res.equals("abcde"));
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/ssh/SshInputScannerTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/ssh/SshInputScannerTests.java
new file mode 100755
index 000000000..5f246fbdc
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/ssh/SshInputScannerTests.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.ssh;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.eclipse.equinox.console.common.ConsoleInputStream;
+import org.eclipse.equinox.console.common.ConsoleOutputStream;
+import org.junit.Assert;
+import org.junit.Test;
+
+
+public class SshInputScannerTests {
+
+ private static final byte ESC = 27;
+
+ @Test
+ public void testScan() throws Exception {
+ ConsoleInputStream in = new ConsoleInputStream();
+ ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+ ConsoleOutputStream out = new ConsoleOutputStream(byteOut);
+ SshInputScanner scanner = new SshInputScanner(in, out);
+ try {
+ scanner.scan((byte) 'a');
+ scanner.scan((byte) 'b');
+ scanner.scan((byte) 'c');
+ } catch (IOException e) {
+ System.out.println("Error while scanning: " + e.getMessage());
+ e.printStackTrace();
+ throw e;
+ }
+
+ String output = byteOut.toString();
+ Assert.assertTrue("Output incorrect. Expected abc, but read " + output, output.equals("abc"));
+ }
+
+ @Test
+ public void testScanESC() throws Exception {
+ ConsoleInputStream in = new ConsoleInputStream();
+ ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+ ConsoleOutputStream out = new ConsoleOutputStream(byteOut);
+ SshInputScanner scanner = new SshInputScanner(in, out);
+
+ try {
+ scanner.scan((byte) 'a');
+ scanner.scan((byte) ESC);
+ scanner.scan((byte) 'b');
+ } catch (IOException e) {
+ System.out.println("Error while scanning: " + e.getMessage());
+ e.printStackTrace();
+ throw e;
+ }
+
+ String output = byteOut.toString();
+ Assert.assertTrue("Output incorrect. Expected ab, but read " + output, output.equals("ab"));
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/ssh/SshShellTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/ssh/SshShellTests.java
new file mode 100755
index 000000000..9c041fdf0
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/ssh/SshShellTests.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.ssh;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.sshd.server.Environment;
+import org.easymock.EasyMock;
+import org.eclipse.equinox.console.common.ConsoleInputStream;
+import org.eclipse.equinox.console.storage.DigestUtil;
+import org.eclipse.equinox.console.storage.SecureUserStore;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+
+
+public class SshShellTests {
+
+ private static final int TEST_CONTENT = 100;
+ private static final String USER_STORE_FILE_NAME = "org.eclipse.equinox.console.jaas.file";
+ private static final String DEFAULT_USER_STORAGE = "osgi.console.ssh.useDefaultSecureStorage";
+ private static final String USER_STORE_NAME = SshShellTests.class.getName() + "_store";
+ private static final String HOST = "localhost";
+ private static final String GOGO_SHELL_COMMAND = "gosh --login --noshutdown";
+ private static final String TERM_PROPERTY = "TERM";
+ private static final String XTERM = "XTERM";
+ private static final String USERNAME = "username";
+ private static final String PASSWORD = "password";
+ private static final String TRUE = "true";
+
+ @Before
+ public void init() throws Exception {
+ clean();
+ initStore();
+ }
+
+ @Test
+ public void testSshConnection() throws Exception {
+ ServerSocket servSocket = null;
+ Socket socketClient = null;
+ Socket socketServer = null;
+ SshShell shell = null;
+ OutputStream outClient = null;
+ OutputStream outServer = null;
+
+ try {
+
+ servSocket = new ServerSocket(0);
+ socketClient = new Socket(HOST, servSocket.getLocalPort());
+ socketServer = servSocket.accept();
+
+ CommandSession session = EasyMock.createMock(CommandSession.class);
+ EasyMock.makeThreadSafe(session, true);
+ session.put((String)EasyMock.anyObject(), EasyMock.anyObject());
+ EasyMock.expectLastCall().times(5);
+ EasyMock.expect(session.execute(GOGO_SHELL_COMMAND)).andReturn(null);
+ session.close();
+ EasyMock.expectLastCall();
+ EasyMock.replay(session);
+
+ CommandProcessor processor = EasyMock.createMock(CommandProcessor.class);
+ EasyMock.expect(processor.createSession((ConsoleInputStream)EasyMock.anyObject(), (PrintStream)EasyMock.anyObject(), (PrintStream)EasyMock.anyObject())).andReturn(session);
+ EasyMock.replay(processor);
+
+ BundleContext context = EasyMock.createMock(BundleContext.class);
+ EasyMock.makeThreadSafe(context, true);
+ EasyMock.expect(context.getProperty(DEFAULT_USER_STORAGE)).andReturn(TRUE);
+ EasyMock.replay(context);
+
+ Map<String, String> environment = new HashMap<String, String>();
+ environment.put(TERM_PROPERTY, XTERM);
+ Environment env = EasyMock.createMock(Environment.class);
+ EasyMock.expect(env.getEnv()).andReturn(environment);
+ EasyMock.replay(env);
+
+ List<CommandProcessor> processors = new ArrayList<CommandProcessor>();
+ processors.add(processor);
+ shell = new SshShell(processors, context);
+ shell.setInputStream(socketServer.getInputStream());
+ shell.setOutputStream(socketServer.getOutputStream());
+ shell.start(env);
+
+ outClient = socketClient.getOutputStream();
+ outClient.write(TEST_CONTENT);
+ outClient.write('\n');
+ outClient.flush();
+ InputStream input = socketClient.getInputStream();
+ int in = input.read();
+ Assert.assertTrue("Server received [" + in + "] instead of " + TEST_CONTENT + " from the ssh client.", in == TEST_CONTENT);
+ } finally {
+ if (socketClient != null) {
+ socketClient.close();
+ }
+ if (outClient != null) {
+ outClient.close();
+ }
+ if (outServer != null) {
+ outServer.close();
+ }
+
+ if (socketServer != null) {
+ socketServer.close();
+ }
+
+ if (servSocket != null) {
+ servSocket.close();
+ }
+
+ }
+ }
+
+ @After
+ public void cleanUp() {
+ clean();
+ }
+
+ private void initStore() throws Exception {
+ System.setProperty(USER_STORE_FILE_NAME, USER_STORE_NAME);
+ SecureUserStore.initStorage();
+ SecureUserStore.putUser(USERNAME, DigestUtil.encrypt(PASSWORD), null);
+ }
+
+ private void clean() {
+ System.setProperty(USER_STORE_FILE_NAME, "");
+ File file = new File(USER_STORE_NAME);
+ if(file.exists()) {
+ file.delete();
+ }
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/ssh/UserAdminCommandTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/ssh/UserAdminCommandTests.java
new file mode 100755
index 000000000..658690178
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/ssh/UserAdminCommandTests.java
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.felix.service.command.CommandSession;
+import org.easymock.EasyMock;
+import org.eclipse.equinox.console.storage.DigestUtil;
+import org.eclipse.equinox.console.storage.SecureUserStore;
+import org.junit.After;
+import org.junit.Test;
+
+
+public class UserAdminCommandTests {
+
+ private static final String USER_STORE_FILE_NAME_PROPERTY = "org.eclipse.equinox.console.jaas.file";
+ private static final String USER_STORE_FILE_NAME = UserAdminCommandTests.class.getName() + "_store";
+ private static final String USERNAME_OPTION = "-username";
+ private static final String PASSWORD_OPTION = "-password";
+ private static final String ROLES_OPTION = "-roles";
+ private static final String USERNAME1 = "username1";
+ private static final String USERNAME2 = "username2";
+ private static final String PASSWORD1 = "password1";
+ private static final String PASSWORD2 = "password2";
+ private static final String ROLES1 = "role1,role2";
+ private static final String ROLES2 = "role3,role4";
+ private static final String ROLES_TO_REMOVE = "role2";
+ private static final String REMAINING_ROLES = "role1";
+
+ @Test
+ public void testCommand() throws Exception {
+ cleanUp();
+
+ System.setProperty(USER_STORE_FILE_NAME_PROPERTY, USER_STORE_FILE_NAME);
+ SecureUserStore.initStorage();
+
+ CommandSession session = EasyMock.createMock(CommandSession.class);
+ session.put((String)EasyMock.anyObject(), EasyMock.anyObject());
+ EasyMock.expectLastCall().times(3);
+ EasyMock.replay(session);
+
+ UserAdminCommand command = new UserAdminCommand();
+ command.addUser(new String[] {USERNAME_OPTION, USERNAME1, PASSWORD_OPTION, PASSWORD1});
+ command.addUser(new String[] {USERNAME_OPTION, USERNAME2, PASSWORD_OPTION, PASSWORD2, ROLES_OPTION, ROLES2});
+
+ String[] usernames = SecureUserStore.getUserNames();
+ boolean arePresent = (usernames[0].equals(USERNAME1) || usernames[0].equals(USERNAME2)) && (usernames[1].equals(USERNAME1) || usernames[1].equals(USERNAME2)) && (!usernames[0].equals(usernames[1]));
+ assertTrue("Usernames not correctly saved", arePresent);
+
+ String pass1 = SecureUserStore.getPassword(USERNAME1);
+ String pass2 = SecureUserStore.getPassword(USERNAME2);
+ assertTrue("Passwords not correctly saved", pass1.equals(DigestUtil.encrypt(PASSWORD1)) && pass2.equals(DigestUtil.encrypt(PASSWORD2)));
+
+ String roles = SecureUserStore.getRoles(USERNAME2);
+ assertEquals("Roles for the second user are not as expected", ROLES2, roles);
+
+ command.addRoles(new String[] {USERNAME_OPTION, USERNAME1, ROLES_OPTION, ROLES1});
+ roles = SecureUserStore.getRoles(USERNAME1);
+ boolean areRolesEqual = compareRoles(ROLES1, roles);
+ assertTrue("Roles for the first user are not as expected", areRolesEqual);
+
+ command.removeRoles(new String[] {USERNAME_OPTION, USERNAME1, ROLES_OPTION, ROLES_TO_REMOVE});
+ roles = SecureUserStore.getRoles(USERNAME1);
+ areRolesEqual = compareRoles(REMAINING_ROLES, roles);
+ assertTrue("Roles for the first user are not as expected", areRolesEqual);
+
+ command.resetPassword(USERNAME1);
+ String pass = SecureUserStore.getPassword(USERNAME1);
+ assertNull("Password should be null", pass);
+
+ command.setPassword(new String[] {USERNAME_OPTION, USERNAME1, PASSWORD_OPTION, PASSWORD1});
+ pass = SecureUserStore.getPassword(USERNAME1);
+ assertEquals("Password should be null", DigestUtil.encrypt(PASSWORD1), pass);
+
+ command.deleteUser(USERNAME2);
+ assertFalse("User2 should not exist", SecureUserStore.existsUser(USERNAME2));
+ }
+
+ @After
+ public void cleanUp() {
+ System.setProperty(USER_STORE_FILE_NAME_PROPERTY, "");
+ File file = new File(USER_STORE_FILE_NAME);
+ if(file.exists()) {
+ file.delete();
+ }
+ }
+
+ private boolean compareRoles(String expectedRoles, String actualRoles) {
+ Set<String> expectedRolesSet = new HashSet<String>();
+ for(String role : expectedRoles.split(",")) {
+ expectedRolesSet.add(role);
+ }
+
+ Set<String> actualRolesSet = new HashSet<String>();
+ for(String role : actualRoles.split(",")) {
+ actualRolesSet.add(role);
+ }
+
+ return expectedRolesSet.equals(actualRolesSet);
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/storage/DigestUtilTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/storage/DigestUtilTests.java
new file mode 100755
index 000000000..d1a040b30
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/storage/DigestUtilTests.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * 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 static org.junit.Assert.*;
+import java.security.MessageDigest;
+
+import org.junit.Test;
+
+
+public class DigestUtilTests {
+
+ private static final char[] HEX_CHARS = "0123456789abcdef".toCharArray();
+ private static final String MD5 = "MD5";
+ private static final String SHA1 = "SHA1";
+ private static final String TEXT = "sometext";
+
+ @Test
+ public void testEncrypt() throws Exception {
+ MessageDigest md = MessageDigest.getInstance(MD5);
+ md.update(TEXT.getBytes());
+ byte[] digest = md.digest();
+
+ char[] chars = new char[2 * digest.length];
+ for (int i = 0; i < digest.length; ++i)
+ {
+ chars[2 * i] = HEX_CHARS[(digest[i] & 0xF0) >>> 4];
+ chars[2 * i + 1] = HEX_CHARS[digest[i] & 0x0F];
+ }
+
+ String modifiedText = TEXT + new String(chars);
+ md = MessageDigest.getInstance(SHA1);
+ md.update(modifiedText.getBytes());
+ digest = md.digest();
+
+ chars = new char[2 * digest.length];
+ for (int i = 0; i < digest.length; ++i)
+ {
+ chars[2 * i] = HEX_CHARS[(digest[i] & 0xF0) >>> 4];
+ chars[2 * i + 1] = HEX_CHARS[digest[i] & 0x0F];
+ }
+
+ String expectedEncryptedText = new String(chars);
+
+ assertEquals("Encrypted text not as expected", expectedEncryptedText, DigestUtil.encrypt(TEXT));
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/storage/SecureUserStoreTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/storage/SecureUserStoreTests.java
new file mode 100755
index 000000000..20062e17b
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/storage/SecureUserStoreTests.java
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * 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 static org.junit.Assert.*;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.equinox.console.jaas.SecureStorageLoginModule;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class SecureUserStoreTests {
+
+ private static final String USER_STORE_FILE_NAME_PROPERTY = "org.eclipse.equinox.console.jaas.file";
+ private static final String USER_STORE_FILE_NAME = SecureUserStoreTests.class.getName() + "_store";
+ private static final String USERNAME1 = "username1";
+ private static final String USERNAME2 = "username2";
+ private static final String PASSWORD1 = "password1";
+ private static final String PASSWORD2 = "password2";
+ private static final String ROLES1 = "role1,role2";
+ private static final String ROLES2 = "role3,role4";
+ private static final String ROLES_TO_REMOVE = "role2";
+ private static final String REMAINING_ROLES = "role1";
+
+ @Test
+ public void testStore() throws Exception {
+ cleanUp();
+
+ System.setProperty(USER_STORE_FILE_NAME_PROPERTY, USER_STORE_FILE_NAME);
+
+ SecureUserStore.initStorage();
+ assertTrue("Secure store file does not exist", new File(USER_STORE_FILE_NAME).exists());
+
+ SecureUserStore.putUser(USERNAME1, PASSWORD1, null);
+ SecureUserStore.putUser(USERNAME2, PASSWORD2, ROLES2);
+
+ String[] usernames = SecureUserStore.getUserNames();
+ boolean arePresent = (usernames[0].equals(USERNAME1) || usernames[0].equals(USERNAME2)) && (usernames[1].equals(USERNAME1) || usernames[1].equals(USERNAME2)) && (!usernames[0].equals(usernames[1]));
+ assertTrue("Usernames not correctly saved", arePresent);
+
+ String pass1 = SecureUserStore.getPassword(USERNAME1);
+ String pass2 = SecureUserStore.getPassword(USERNAME2);
+ assertTrue("Passwords not correctly saved", pass1.equals(PASSWORD1) && pass2.equals(PASSWORD2));
+
+ boolean existsUser1 = SecureUserStore.existsUser(USERNAME1);
+ boolean existsUser2 = SecureUserStore.existsUser(USERNAME2);
+ assertTrue("Users should exist", existsUser1 && existsUser2);
+
+ String roles = SecureUserStore.getRoles(USERNAME2);
+ assertEquals("Roles for the second user are not as expected", ROLES2, roles);
+
+ SecureUserStore.addRoles(USERNAME1, ROLES1);
+ roles = SecureUserStore.getRoles(USERNAME1);
+ boolean areRolesEqual = compareRoles(ROLES1, roles);
+ assertTrue("Roles for the first user are not as expected", areRolesEqual);
+
+ SecureUserStore.removeRoles(USERNAME1, ROLES_TO_REMOVE);
+ roles = SecureUserStore.getRoles(USERNAME1);
+ areRolesEqual = compareRoles(REMAINING_ROLES, roles);
+ assertTrue("Roles for the first user are not as expected", areRolesEqual);
+
+ SecureUserStore.resetPassword(USERNAME1);
+ String pass = SecureUserStore.getPassword(USERNAME1);
+ assertNull("Password should be null", pass);
+
+ SecureUserStore.setPassword(USERNAME1, PASSWORD1);
+ pass = SecureUserStore.getPassword(USERNAME1);
+ assertEquals("Password should be null", PASSWORD1, pass);
+
+ SecureUserStore.deleteUser(USERNAME2);
+ assertFalse("User2 should not exist", SecureUserStore.existsUser(USERNAME2));
+ }
+
+ @After
+ public void cleanUp() {
+ System.setProperty(USER_STORE_FILE_NAME_PROPERTY, "");
+ File file = new File(USER_STORE_FILE_NAME);
+ if(file.exists()) {
+ file.delete();
+ }
+ }
+
+ private boolean compareRoles(String expectedRoles, String actualRoles) {
+ Set<String> expectedRolesSet = new HashSet<String>();
+ for(String role : expectedRoles.split(",")) {
+ expectedRolesSet.add(role);
+ }
+
+ Set<String> actualRolesSet = new HashSet<String>();
+ for(String role : actualRoles.split(",")) {
+ actualRolesSet.add(role);
+ }
+
+ return expectedRolesSet.equals(actualRolesSet);
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/supportability/ConsoleInputHandlerTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/supportability/ConsoleInputHandlerTests.java
new file mode 100755
index 000000000..ad4599070
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/supportability/ConsoleInputHandlerTests.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * 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 contribution
+ ******************************************************************************/
+
+package org.eclipse.equinox.console.supportability;
+
+import org.eclipse.equinox.console.common.ConsoleInputStream;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+
+public class ConsoleInputHandlerTests {
+
+ private static final long WAIT_TIME = 10000;
+
+ @Test
+ public void testHandler() throws Exception {
+ PipedInputStream input = new PipedInputStream();
+ PipedOutputStream output = new PipedOutputStream(input);
+ ConsoleInputStream in = new ConsoleInputStream();
+ ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+ ConsoleInputHandler handler = new ConsoleInputHandler(input, in, byteOut);
+ byte[] testInput = new byte[] { 'a', 'b', 'c', 'd', 'e', '\r', '\n' };
+ byte[] expected = new byte[] { 'a', 'b', 'c', 'd', 'e', '\n' };
+ output.write(testInput);
+ output.flush();
+ handler.start();
+
+ try {
+ Thread.sleep(WAIT_TIME);
+ } catch (Exception e) {
+ // do nothing
+ }
+
+ byte[] read = new byte[expected.length];
+ in.read(read, 0, expected.length);
+ for (int i = 0; i < expected.length; i++) {
+ Assert.assertEquals("Incorrect char read. Position " + i + ", expected " + expected[i] + ", read " + read[i], expected[i], read[i]);
+ }
+
+ output.close();
+ input.close();
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/supportability/ConsoleInputScannerTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/supportability/ConsoleInputScannerTests.java
new file mode 100755
index 000000000..2240e2c65
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/supportability/ConsoleInputScannerTests.java
@@ -0,0 +1,257 @@
+/*******************************************************************************
+ * 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 contribution
+ ******************************************************************************/
+
+package org.eclipse.equinox.console.supportability;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+
+import org.apache.felix.service.command.CommandSession;
+import org.eclipse.equinox.console.common.ConsoleInputStream;
+import org.eclipse.equinox.console.common.ConsoleOutputStream;
+import org.eclipse.equinox.console.common.KEYS;
+import org.eclipse.equinox.console.common.terminal.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.completion.common.Completer;
+import org.junit.Assert;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+
+import java.io.ByteArrayOutputStream;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+public class ConsoleInputScannerTests {
+
+ private static int BS;
+
+ private static final int LF = 10;
+
+ private static final int CR = 13;
+
+ private static final int ESC = 27;
+
+ private static int DELL;
+
+ private static int TAB = 9;
+
+ private static final String COMMANDS = ".commands";
+
+ @Test
+ public void test() throws Exception {
+ Set<TerminalTypeMappings> supportedEscapeSequences = new HashSet<TerminalTypeMappings>();
+ supportedEscapeSequences.add(new ANSITerminalTypeMappings());
+ supportedEscapeSequences.add(new VT100TerminalTypeMappings());
+ supportedEscapeSequences.add(new VT220TerminalTypeMappings());
+ supportedEscapeSequences.add(new VT320TerminalTypeMappings());
+ supportedEscapeSequences.add(new SCOTerminalTypeMappings());
+
+ for (TerminalTypeMappings ttMappings : supportedEscapeSequences) {
+ Map<String, KEYS> escapesToKey = ttMappings.getEscapesToKey();
+ Map<KEYS, byte[]> keysToEscapes = new HashMap<KEYS, byte[]>();
+ for (Entry<String, KEYS> entry : escapesToKey.entrySet()) {
+ keysToEscapes.put(entry.getValue(), entry.getKey().getBytes());
+ }
+
+ BS = ttMappings.getBackspace();
+ DELL = ttMappings.getDel();
+
+ testScan(ttMappings, keysToEscapes);
+ }
+ }
+
+ private void testScan(TerminalTypeMappings mappings, Map<KEYS, byte[]> keysToEscapes) throws Exception {
+ ConsoleInputStream in = new ConsoleInputStream();
+ ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+ ConsoleOutputStream out = new ConsoleOutputStream(byteOut);
+ ConsoleInputScanner scanner = new ConsoleInputScanner(in, out);
+ scanner.setBackspace(mappings.getBackspace());
+ scanner.setCurrentEscapesToKey(mappings.getEscapesToKey());
+ scanner.setDel(mappings.getDel());
+ scanner.setEscapes(mappings.getEscapes());
+
+ byte[] line1 = new byte[] { 'a', 'b', 'c', 'd', 'e' };
+ byte[] line2 = new byte[] { 't', 'e', 's', 't' };
+ byte[] line3 = new byte[] { 'l', 'a', 's', 't' };
+
+ addLine(scanner, line1);
+ checkInpusStream(in, line1);
+
+ addLine(scanner, line2);
+ checkInpusStream(in, line2);
+
+ addLine(scanner, line3);
+ checkInpusStream(in, line3);
+
+ add(scanner, keysToEscapes.get(KEYS.UP));
+ add(scanner, keysToEscapes.get(KEYS.UP));
+ String res = byteOut.toString();
+ Assert.assertTrue("Error processing up arrow; expected test, actual " + res.substring(res.length() - 4), res.endsWith("test"));
+
+ add(scanner, keysToEscapes.get(KEYS.DOWN));
+ res = byteOut.toString();
+ Assert.assertTrue("Error processing down arrow; expected last, actual " + res.substring(res.length() - 4), res.endsWith("last"));
+
+ add(scanner, keysToEscapes.get(KEYS.PGUP));
+ res = byteOut.toString();
+ Assert.assertTrue("Error processing PageUp; expected abcde, actual " + res.substring(res.length() - 4), res.endsWith("abcde"));
+
+ add(scanner, keysToEscapes.get(KEYS.PGDN));
+ res = byteOut.toString();
+ Assert.assertTrue("Error processing PageDown; expected last, actual " + res.substring(res.length() - 4), res.endsWith("last"));
+
+ if (BS > 0) {
+ scanner.scan(BS);
+ res = byteOut.toString();
+ Assert.assertTrue("Error processing backspace; expected las, actual " + res.substring(res.length() - 3), res.endsWith("las"));
+ scanner.scan('t');
+ }
+
+ if (DELL > 0) {
+ add(scanner, keysToEscapes.get(KEYS.LEFT));
+ scanner.scan(DELL);
+ res = byteOut.toString();
+ Assert.assertTrue("Error processing del; expected las, actual " + res.substring(res.length() - 3), res.endsWith("las"));
+ scanner.scan('t');
+ }
+
+ add(scanner, keysToEscapes.get(KEYS.LEFT));
+ add(scanner, keysToEscapes.get(KEYS.LEFT));
+ add(scanner, keysToEscapes.get(KEYS.RIGHT));
+ if (DELL > 0) {
+ scanner.scan(DELL);
+ } else {
+ add(scanner, keysToEscapes.get(KEYS.DEL));
+ }
+ res = byteOut.toString();
+ Assert.assertTrue("Error processing arrows; expected las, actual " + res.substring(res.length() - 3), res.endsWith("las"));
+ scanner.scan('t');
+
+ if (keysToEscapes.get(KEYS.DEL) != null) {
+ add(scanner, keysToEscapes.get(KEYS.LEFT));
+ add(scanner, keysToEscapes.get(KEYS.DEL));
+ res = byteOut.toString();
+ Assert.assertTrue("Error processing delete; expected las, actual " + res.substring(res.length() - 3), res.endsWith("las"));
+ scanner.scan('t');
+ }
+
+ add(scanner, keysToEscapes.get(KEYS.HOME));
+ if (DELL > 0) {
+ scanner.scan(DELL);
+ } else {
+ add(scanner, keysToEscapes.get(KEYS.DEL));
+ }
+ res = byteOut.toString();
+ res = res.substring(res.length() - 6, res.length() - 3);
+ Assert.assertTrue("Error processing Home; expected ast, actual " + res, res.equals("ast"));
+ scanner.scan('l');
+
+ add(scanner, keysToEscapes.get(KEYS.END));
+ add(scanner, keysToEscapes.get(KEYS.LEFT));
+ if (DELL > 0) {
+ scanner.scan(DELL);
+ } else {
+ add(scanner, keysToEscapes.get(KEYS.DEL));
+ }
+ res = byteOut.toString();
+ Assert.assertTrue("Error processing End; expected las, actual " + res.substring(res.length() - 3), res.endsWith("las"));
+ scanner.scan('t');
+
+ add(scanner, keysToEscapes.get(KEYS.LEFT));
+ add(scanner, keysToEscapes.get(KEYS.INS));
+ scanner.scan('a');
+ res = byteOut.toString();
+ Assert.assertTrue("Error processing Ins; expected las, actual " + res.substring(res.length() - 4), res.endsWith("lasa"));
+
+ BundleContext context = createMock(BundleContext.class);
+ expect(context.getServiceReferences(Completer.class.getName(), null)).andReturn(null).anyTimes();
+ replay(context);
+
+ Set<String> commands = new HashSet<String>();
+ commands.add("equinox:bundles");
+ commands.add("equinox:bundle");
+ commands.add("gogo:bundlebylocation");
+ commands.add("gogo:bundlelevel");
+ commands.add("equinox:headers");
+
+ CommandSession session = createMock(CommandSession.class);
+ expect(session.get(COMMANDS)).andReturn(commands).anyTimes();
+ replay(session);
+
+ scanner.setContext(context);
+ scanner.setSession(session);
+
+ scanner.scan(CR);
+ scanner.scan(LF);
+ scanner.scan('b');
+ scanner.scan('u');
+ scanner.scan('n');
+ scanner.scan(TAB);
+ res = byteOut.toString();
+ Assert.assertTrue("Expected completion suggestion is not contained in the output", res.contains("bundles\r\n"));
+ Assert.assertTrue("Expected completion suggestion is not contained in the output", res.contains("bundle\r\n"));
+ Assert.assertTrue("bun should be completed to bundle", res.endsWith("bundle"));
+ Assert.assertTrue("Expected completion suggestion is not contained in the output", res.contains("bundlebylocation\r\n"));
+ Assert.assertTrue("Expected completion suggestion is not contained in the output", res.contains("bundlelevel\r\n"));
+ Assert.assertFalse("Not expected completion suggestion", res.contains("headers\r\n"));
+ }
+
+ private static void addLine(ConsoleInputScanner scanner, byte[] line) throws Exception {
+ for (byte b : line) {
+ try {
+ scanner.scan(b);
+ } catch (Exception e) {
+ System.out.println("Error scanning symbol " + b);
+ throw new Exception("Error scanning symbol" + b);
+ }
+ }
+
+ try {
+ scanner.scan(CR);
+ } catch (Exception e) {
+ System.out.println("Error scanning symbol " + CR);
+ throw new Exception("Error scanning symbol " + CR);
+ }
+
+ try {
+ scanner.scan(LF);
+ } catch (Exception e) {
+ System.out.println("Error scanning symbol " + LF);
+ throw new Exception("Error scanning symbol " + LF);
+ }
+ }
+
+ private void add(ConsoleInputScanner scanner, byte[] sequence) throws Exception {
+ scanner.scan(ESC);
+ for (byte b : sequence) {
+ scanner.scan(b);
+ }
+ }
+
+ private void checkInpusStream(ConsoleInputStream in, byte[] expected) throws Exception {
+ // the actual number of bytes in the stream is two more than the bytes in the array, because of the CR and LF
+ // symbols, added after the array
+ byte[] read = new byte[expected.length + 1];
+ in.read(read, 0, read.length);
+ for (int i = 0; i < expected.length; i++) {
+ Assert.assertEquals("Incorrect char read. Position " + i + ", expected " + expected[i] + ", read " + read[i], expected[i], read[i]);
+ }
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/NegotiationFinishedCallbackTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/NegotiationFinishedCallbackTests.java
new file mode 100755
index 000000000..225dfdb09
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/NegotiationFinishedCallbackTests.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * 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 contribution
+ ******************************************************************************/
+
+package org.eclipse.equinox.console.telnet;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class NegotiationFinishedCallbackTests {
+
+ @Test
+ public void finishTest() throws Exception {
+ TelnetConnection telnetConnection = null;
+ telnetConnection = new TelnetConnection (null, null, null);
+ NegotiationFinishedCallback callback = new NegotiationFinishedCallback(telnetConnection);
+ callback.finished();
+ Assert.assertTrue("Finished not called on console session", telnetConnection.isTelnetNegotiationFinished);
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetCommandTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetCommandTests.java
new file mode 100755
index 000000000..b32a55b70
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetCommandTests.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * 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.telnet;
+
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.net.Socket;
+import java.util.Dictionary;
+
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.CommandSession;
+import org.easymock.EasyMock;
+import org.eclipse.equinox.console.common.ConsoleInputStream;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+
+
+public class TelnetCommandTests {
+
+ private static final int TEST_CONTENT = 100;
+ private static final String TELNET_PORT_PROP_NAME = "osgi.console";
+ private static final String USE_CONFIG_ADMIN_PROP = "osgi.console.useConfigAdmin";
+ private static final String STOP_COMMAND = "stop";
+ private static final String HOST = "localhost";
+ private static final String FALSE = "false";
+ private static final int TELNET_PORT = 2223;
+ private static final long WAIT_TIME = 5000;
+
+ @Test
+ public void testTelnetCommand() throws Exception {
+ CommandSession session = EasyMock.createMock(CommandSession.class);
+ session.put((String)EasyMock.anyObject(), EasyMock.anyObject());
+ EasyMock.expectLastCall().times(3);
+ EasyMock.expect(session.execute((String)EasyMock.anyObject())).andReturn(new Object());
+ session.close();
+ EasyMock.expectLastCall();
+ EasyMock.replay(session);
+
+ CommandProcessor processor = EasyMock.createMock(CommandProcessor.class);
+ EasyMock.expect(processor.createSession((ConsoleInputStream)EasyMock.anyObject(), (PrintStream)EasyMock.anyObject(), (PrintStream)EasyMock.anyObject())).andReturn(session);
+ EasyMock.replay(processor);
+
+ BundleContext context = EasyMock.createMock(BundleContext.class);
+ EasyMock.expect(context.getProperty(USE_CONFIG_ADMIN_PROP)).andReturn(FALSE);
+ EasyMock.expect(context.getProperty(TELNET_PORT_PROP_NAME)).andReturn(Integer.toString(TELNET_PORT));
+ EasyMock.expect(context.registerService((String)EasyMock.anyObject(), EasyMock.anyObject(), (Dictionary<String, ?>)EasyMock.anyObject())).andReturn(null);
+ EasyMock.replay(context);
+
+ TelnetCommand command = new TelnetCommand(processor, context);
+ command.start();
+
+ Socket socketClient = null;
+ try {
+ socketClient = new Socket(HOST, TELNET_PORT);
+ OutputStream outClient = socketClient.getOutputStream();
+ outClient.write(TEST_CONTENT);
+ outClient.write('\n');
+ outClient.flush();
+
+ // wait for the accept thread to finish execution
+ try {
+ Thread.sleep(WAIT_TIME);
+ } catch (InterruptedException ie) {
+ // do nothing
+ }
+ } finally {
+ if (socketClient != null) {
+ socketClient.close();
+ }
+ command.telnet(new String[] {STOP_COMMAND});
+ }
+ EasyMock.verify(context);
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetCommandWithConfigAdminTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetCommandWithConfigAdminTests.java
new file mode 100755
index 000000000..2eb207297
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetCommandWithConfigAdminTests.java
@@ -0,0 +1,196 @@
+package org.eclipse.equinox.console.telnet;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.net.Socket;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import junit.framework.Assert;
+
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.CommandSession;
+import org.easymock.EasyMock;
+import org.easymock.IAnswer;
+import org.eclipse.equinox.console.common.ConsoleInputStream;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.BundleListener;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.cm.ManagedService;
+
+
+public class TelnetCommandWithConfigAdminTests {
+ private static final int TEST_CONTENT = 100;
+ private static final String STOP_COMMAND = "stop";
+ private static final String HOST = "localhost";
+ private static final String TELNET_PORT = "2223";
+ private static final long WAIT_TIME = 5000;
+ private static final String USE_CONFIG_ADMIN_PROP = "osgi.console.useConfigAdmin";
+ private static final String TRUE = "true";
+ private static final String FALSE = "false";
+ private ManagedService configurator;
+
+ @Test
+ public void testTelnetCommandWithConfigAdminEnabledTelnet() throws Exception {
+ CommandSession session = EasyMock.createMock(CommandSession.class);
+ session.put((String)EasyMock.anyObject(), EasyMock.anyObject());
+ EasyMock.expectLastCall().times(3);
+ EasyMock.expect(session.execute((String)EasyMock.anyObject())).andReturn(new Object());
+ session.close();
+ EasyMock.expectLastCall();
+ EasyMock.replay(session);
+
+ CommandProcessor processor = EasyMock.createMock(CommandProcessor.class);
+ EasyMock.expect(processor.createSession((ConsoleInputStream)EasyMock.anyObject(), (PrintStream)EasyMock.anyObject(), (PrintStream)EasyMock.anyObject())).andReturn(session);
+ EasyMock.replay(processor);
+
+ final ServiceRegistration<?> registration = EasyMock.createMock(ServiceRegistration.class);
+ registration.setProperties((Dictionary)EasyMock.anyObject());
+
+ EasyMock.expectLastCall();
+ EasyMock.replay(registration);
+
+ BundleContext context = EasyMock.createMock(BundleContext.class);
+ EasyMock.expect(context.getProperty(USE_CONFIG_ADMIN_PROP)).andReturn(TRUE);
+ EasyMock.expect(
+ (ServiceRegistration) context.registerService(
+ (String)EasyMock.anyObject(),
+ (ManagedService)EasyMock.anyObject(),
+ (Dictionary<String, ?>)EasyMock.anyObject())
+ ).andAnswer((IAnswer<ServiceRegistration<?>>) new IAnswer<ServiceRegistration<?>>() {
+ public ServiceRegistration<?> answer() {
+ configurator = (ManagedService) EasyMock.getCurrentArguments()[1];
+ return registration;
+ }
+ });
+ EasyMock.expect(
+ context.registerService(
+ (String)EasyMock.anyObject(),
+ (TelnetCommand)EasyMock.anyObject(),
+ (Dictionary<String, ?>)EasyMock.anyObject())).andReturn(null);
+ EasyMock.replay(context);
+
+ TelnetCommand command = new TelnetCommand(processor, context);
+ command.start();
+ Dictionary props = new Hashtable();
+ props.put("port", TELNET_PORT);
+ props.put("host", HOST);
+ props.put("enabled", TRUE);
+ configurator.updated(props);
+
+ Socket socketClient = null;
+ try {
+ socketClient = new Socket(HOST, Integer.parseInt(TELNET_PORT));
+ OutputStream outClient = socketClient.getOutputStream();
+ outClient.write(TEST_CONTENT);
+ outClient.write('\n');
+ outClient.flush();
+
+ // wait for the accept thread to finish execution
+ try {
+ Thread.sleep(WAIT_TIME);
+ } catch (InterruptedException ie) {
+ // do nothing
+ }
+ } finally {
+ if (socketClient != null) {
+ socketClient.close();
+ }
+ command.telnet(new String[] {STOP_COMMAND});
+ }
+ EasyMock.verify(context);
+ }
+
+ @Test
+ public void testTelnetCommandWithConfigAdminDisabledTelnet() throws Exception {
+ disabledTelnet(false);
+ }
+
+ @Test
+ public void testTelnetCommandWithConfigAdminDisabledTelnetByDefault() throws Exception {
+ disabledTelnet(true);
+ }
+
+ private void disabledTelnet(boolean isDefault) throws Exception {
+ CommandSession session = EasyMock.createMock(CommandSession.class);
+ session.put((String)EasyMock.anyObject(), EasyMock.anyObject());
+ EasyMock.expectLastCall().times(4);
+ EasyMock.expect(session.execute((String)EasyMock.anyObject())).andReturn(new Object());
+ session.close();
+ EasyMock.expectLastCall();
+ EasyMock.replay(session);
+
+ CommandProcessor processor = EasyMock.createMock(CommandProcessor.class);
+ EasyMock.expect(processor.createSession((ConsoleInputStream)EasyMock.anyObject(), (PrintStream)EasyMock.anyObject(), (PrintStream)EasyMock.anyObject())).andReturn(session);
+ EasyMock.replay(processor);
+
+ final ServiceRegistration<?> registration = EasyMock.createMock(ServiceRegistration.class);
+ registration.setProperties((Dictionary)EasyMock.anyObject());
+
+ EasyMock.expectLastCall();
+ EasyMock.replay(registration);
+
+ BundleContext context = EasyMock.createMock(BundleContext.class);
+ EasyMock.expect(context.getProperty(USE_CONFIG_ADMIN_PROP)).andReturn(TRUE);
+ EasyMock.expect(
+ (ServiceRegistration) context.registerService(
+ (String)EasyMock.anyObject(),
+ (ManagedService)EasyMock.anyObject(),
+ (Dictionary<String, ?>)EasyMock.anyObject())
+ ).andAnswer((IAnswer<ServiceRegistration<?>>) new IAnswer<ServiceRegistration<?>>() {
+ public ServiceRegistration<?> answer() {
+ configurator = (ManagedService) EasyMock.getCurrentArguments()[1];
+ return registration;
+ }
+ });
+ EasyMock.expect(
+ context.registerService(
+ (String)EasyMock.anyObject(),
+ (TelnetCommand)EasyMock.anyObject(),
+ (Dictionary<String, ?>)EasyMock.anyObject())).andReturn(null);
+ EasyMock.replay(context);
+
+ TelnetCommand command = new TelnetCommand(processor, context);
+ command.start();
+ Dictionary props = new Hashtable();
+ props.put("port", TELNET_PORT);
+ props.put("host", HOST);
+ if (isDefault == false) {
+ props.put("enabled", FALSE);
+ }
+ configurator.updated(props);
+
+ Socket socketClient = null;
+ try {
+ socketClient = new Socket(HOST, Integer.parseInt(TELNET_PORT));
+ Assert.fail("It should not be possible to open a socket to " + HOST + ":" + TELNET_PORT);
+ } catch (IOException e) {
+ // this is ok, there should be an exception
+ } finally {
+ if (socketClient != null) {
+ socketClient.close();
+ }
+ try {
+ command.telnet(new String[] {STOP_COMMAND});
+ } catch (IllegalStateException e) {
+ //this is expected
+ }
+ }
+ EasyMock.verify(context);
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetConnectionTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetConnectionTests.java
new file mode 100755
index 000000000..c133ead89
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetConnectionTests.java
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * 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 contribution
+ ******************************************************************************/
+
+package org.eclipse.equinox.console.telnet;
+
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.CommandSession;
+import org.junit.Assert;
+import org.junit.Test;
+import org.easymock.EasyMock;
+import org.eclipse.equinox.console.common.ConsoleInputStream;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+public class TelnetConnectionTests {
+
+ private static final String HOST = "localhost";
+ private static final int TEST_CONTENT = 100;
+ private static final int IAC = 255;
+
+ @Test
+ public void testTelneConnection() throws Exception {
+ ServerSocket servSocket = null;
+ Socket socketClient = null;
+ Socket socketServer = null;
+ TelnetConnection connection = null;
+ OutputStream outClient = null;
+ OutputStream outServer = null;
+
+ try {
+ servSocket = new ServerSocket(0);
+ socketClient = new Socket(HOST, servSocket.getLocalPort());
+ socketServer = servSocket.accept();
+
+ CommandSession session = EasyMock.createMock(CommandSession.class);
+ session.put((String)EasyMock.anyObject(), EasyMock.anyObject());
+ EasyMock.expectLastCall().times(3);
+ EasyMock.expect(session.execute((String)EasyMock.anyObject())).andReturn(null);
+ session.close();
+ EasyMock.expectLastCall();
+ EasyMock.replay(session);
+
+ CommandProcessor processor = EasyMock.createMock(CommandProcessor.class);
+ EasyMock.expect(processor.createSession((ConsoleInputStream) EasyMock.anyObject(), (PrintStream) EasyMock.anyObject(), (PrintStream) EasyMock.anyObject())).andReturn(session);
+ EasyMock.replay(processor);
+
+ connection = new TelnetConnection(socketServer, processor, null);
+ connection.start();
+
+ outClient = socketClient.getOutputStream();
+ outClient.write(TEST_CONTENT);
+ outClient.write('\n');
+ outClient.flush();
+
+ InputStream input = socketServer.getInputStream();
+ int in = input.read();
+ Assert.assertTrue("Server received [" + in + "] instead of " + TEST_CONTENT + " from the telnet client.", in == TEST_CONTENT);
+
+ input = socketClient.getInputStream();
+ in = input.read();
+ // here IAC is expected, since when the output stream in TelnetConsoleSession is created, several telnet
+ // commands are written to it, each of them starting with IAC
+ Assert.assertTrue("Client receive telnet responses from the server unexpected value [" + in + "] instead of " + IAC + ".", in == IAC);
+ connection.telnetNegotiationFinished();
+ Thread.sleep(5000);
+ EasyMock.verify(session, processor);
+ } finally {
+ if (socketClient != null) {
+ socketClient.close();
+ }
+ if (outClient != null) {
+ outClient.close();
+ }
+ if (outServer != null) {
+ outServer.close();
+ }
+
+ if (socketServer != null) {
+ socketServer.close();
+ }
+
+ if (servSocket != null) {
+ servSocket.close();
+ }
+ }
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetDisconnectionTest.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetDisconnectionTest.java
new file mode 100755
index 000000000..810d1f524
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetDisconnectionTest.java
@@ -0,0 +1,103 @@
+package org.eclipse.equinox.console.telnet;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.io.PrintStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.CommandSession;
+import org.easymock.EasyMock;
+import org.eclipse.equinox.console.commands.DisconnectCommand;
+import org.eclipse.equinox.console.common.ConsoleInputStream;
+import org.junit.Assert;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+
+public class TelnetDisconnectionTest {
+ private static final String HOST = "localhost";
+ private InputStream in;
+
+ @Test
+ public void testTelneConnection() throws Exception {
+ ServerSocket servSocket = null;
+ Socket socketClient = null;
+ Socket socketServer = null;
+ TelnetConnection connection = null;
+ OutputStream outClient = null;
+ OutputStream outServer = null;
+
+ try {
+ servSocket = new ServerSocket(0);
+ socketClient = new Socket(HOST, servSocket.getLocalPort());
+ socketServer = servSocket.accept();
+
+ CommandProcessor processor = EasyMock.createMock(CommandProcessor.class);
+ connection = new TelnetConnection(socketServer, processor, null);
+
+ final CommandSession session = EasyMock.createMock(CommandSession.class);
+ EasyMock.makeThreadSafe(session, true);
+ session.put((String)EasyMock.anyObject(), EasyMock.anyObject());
+ EasyMock.expectLastCall().times(3);
+ EasyMock.expect(session.get("CLOSEABLE")).andReturn(connection);
+ EasyMock.expect(session.execute((String)EasyMock.anyObject())).andReturn(null);
+ session.close();
+ EasyMock.expectLastCall();
+ EasyMock.replay(session);
+
+ EasyMock.expect(processor.createSession((ConsoleInputStream) EasyMock.anyObject(), (PrintStream) EasyMock.anyObject(), (PrintStream) EasyMock.anyObject())).andReturn(session);
+ EasyMock.replay(processor);
+
+ connection.start();
+
+ BundleContext context = EasyMock.createMock(BundleContext.class);
+ final DisconnectCommand command = new DisconnectCommand(context);
+
+ PipedOutputStream outputStream = new PipedOutputStream();
+ PipedInputStream inputStream = new PipedInputStream(outputStream);
+
+ in = System.in;
+ System.setIn(inputStream);
+
+ new Thread() {
+ public void run() {
+ command.disconnect(session);
+ }
+ }.start();
+
+ outputStream.write(new byte[]{'y'});
+ outputStream.write('\n');
+ outputStream.flush();
+
+ Thread.sleep(3000);
+ Assert.assertTrue("Socket is not closed!", socketServer.isClosed());
+
+ connection.telnetNegotiationFinished();
+ Thread.sleep(5000);
+ EasyMock.verify(session, processor);
+ } finally {
+ if (socketClient != null) {
+ socketClient.close();
+ }
+ if (outClient != null) {
+ outClient.close();
+ }
+ if (outServer != null) {
+ outServer.close();
+ }
+
+ if (socketServer != null) {
+ socketServer.close();
+ }
+
+ if (servSocket != null) {
+ servSocket.close();
+ }
+
+ System.setIn(in);
+ }
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetInputHandlerTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetInputHandlerTests.java
new file mode 100755
index 000000000..f5d3bd6a8
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetInputHandlerTests.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * 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 contribution
+ ******************************************************************************/
+
+package org.eclipse.equinox.console.telnet;
+
+import org.eclipse.equinox.console.common.ConsoleInputStream;
+import org.eclipse.equinox.console.common.ConsoleOutputStream;
+import org.junit.Assert;
+import org.junit.Test;
+
+import static org.easymock.EasyMock.*;
+
+import java.io.ByteArrayOutputStream;
+import java.io.StringBufferInputStream;
+
+public class TelnetInputHandlerTests {
+
+ private static final long WAIT_TIME = 10000;
+
+ @Test
+ public void testHandler() throws Exception {
+ StringBufferInputStream input = new StringBufferInputStream("abcde");
+ ConsoleInputStream in = new ConsoleInputStream();
+ ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+ ConsoleOutputStream out = new ConsoleOutputStream(byteOut);
+ Callback callback = createMock(Callback.class);
+ TelnetInputHandler handler = new TelnetInputHandler(input, in, out, callback);
+ handler.start();
+
+ // wait for the accept thread to start execution
+ try {
+ Thread.sleep(WAIT_TIME);
+ } catch (InterruptedException ie) {
+ // do nothing
+ }
+
+ String res = byteOut.toString();
+ Assert.assertTrue("Wrong input. Expected abcde, read " + res, res.equals("abcde"));
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetInputScannerTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetInputScannerTests.java
new file mode 100755
index 000000000..1a9290484
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetInputScannerTests.java
@@ -0,0 +1,224 @@
+/*******************************************************************************
+ * 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 contribution
+ ******************************************************************************/
+
+package org.eclipse.equinox.console.telnet;
+
+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.junit.Assert;
+import org.junit.Test;
+
+import static org.easymock.EasyMock.*;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+public class TelnetInputScannerTests {
+
+ private static final int IAC = 255;
+
+ private static final int DO = 253;
+
+ private static final int DO_NOT = 254;
+
+ private static final int TTYPE = 24;
+
+ private static final int WILL = 251;
+
+ private static final int WILL_NOT = 252;
+
+ private static final int SB = 250;
+
+ private static final int SE = 240;
+
+ private static final int EL = 248;
+
+ private static final int SEND = 1;
+
+ private static final int IS = 0;
+
+ protected static final byte ESC = 27;
+
+ @Test
+ public void testScan() throws Exception {
+ ConsoleInputStream in = new ConsoleInputStream();
+ ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+ ConsoleOutputStream out = new ConsoleOutputStream(byteOut);
+ Callback callback = createMock(Callback.class);
+ TelnetInputScanner scanner = new TelnetInputScanner(in, out, callback);
+ try {
+ scanner.scan((byte) SE);
+ scanner.scan((byte) EL);
+ scanner.scan((byte) SB);
+ scanner.scan((byte) WILL);
+ scanner.scan((byte) WILL_NOT);
+ scanner.scan((byte) DO);
+ scanner.scan((byte) DO_NOT);
+ scanner.scan((byte) 'a');
+ scanner.scan((byte) 'b');
+ scanner.scan((byte) 'c');
+ } catch (IOException e) {
+ System.out.println("Error while scanning: " + e.getMessage());
+ e.printStackTrace();
+ throw e;
+ }
+
+ String output = byteOut.toString();
+ Assert.assertTrue("Output incorrect. Expected abc, but read " + output, output.equals("abc"));
+ }
+
+ @Test
+ public void testScanESC() throws Exception {
+ ConsoleInputStream in = new ConsoleInputStream();
+ ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+ ConsoleOutputStream out = new ConsoleOutputStream(byteOut);
+ Callback callback = createMock(Callback.class);
+ TelnetInputScanner scanner = new TelnetInputScanner(in, out, callback);
+
+ try {
+ scanner.scan((byte) 'a');
+ scanner.scan((byte) ESC);
+ scanner.scan((byte) 'b');
+ } catch (IOException e) {
+ System.out.println("Error while scanning: " + e.getMessage());
+ e.printStackTrace();
+ throw e;
+ }
+
+ String output = byteOut.toString();
+ Assert.assertTrue("Output incorrect. Expected ab, but read " + output, output.equals("ab"));
+ }
+
+ @Test
+ public void testTTNegotiations() throws Exception {
+ Map<byte[], TerminalTypeMappings> ttMappings = new HashMap<byte[], TerminalTypeMappings>();
+ ttMappings.put(new byte[] { 'A', 'N', 'S', 'I' }, new ANSITerminalTypeMappings());
+ ttMappings.put(new byte[] { 'V', 'T', '1', '0', '0' }, new VT100TerminalTypeMappings());
+ ttMappings.put(new byte[] { 'V', 'T', '2', '2', '0' }, new VT220TerminalTypeMappings());
+ ttMappings.put(new byte[] { 'X', 'T', 'E', 'R', 'M' }, new VT220TerminalTypeMappings());
+ ttMappings.put(new byte[] { 'V', 'T', '3', '2', '0' }, new VT320TerminalTypeMappings());
+ ttMappings.put(new byte[] { 'S', 'C', 'O' }, new SCOTerminalTypeMappings());
+
+ for (byte[] ttype : ttMappings.keySet()) {
+ testTerminalTypeNegotiation(ttype, ttMappings.get(ttype));
+ }
+ }
+
+ private void testTerminalTypeNegotiation(byte[] terminalType, TerminalTypeMappings mappings) throws Exception {
+ PipedInputStream clientIn = new PipedInputStream();
+ PipedOutputStream serverOut = new PipedOutputStream(clientIn);
+
+ byte[] requestNegotiation = { (byte) IAC, (byte) DO, (byte) TTYPE };
+
+ TestCallback testCallback = new TestCallback();
+ TelnetOutputStream out = new TelnetOutputStream(serverOut);
+ TelnetInputScanner scanner = new TelnetInputScanner(new ConsoleInputStream(), out, testCallback);
+ out.write(requestNegotiation);
+ out.flush();
+
+ int read = clientIn.read();
+ Assert.assertEquals("Unexpected input ", IAC, read);
+ read = clientIn.read();
+ Assert.assertEquals("Unexpected input ", DO, read);
+ read = clientIn.read();
+ Assert.assertEquals("Unexpected input ", TTYPE, read);
+
+ scanner.scan(IAC);
+ scanner.scan(WILL);
+ scanner.scan(TTYPE);
+
+ read = clientIn.read();
+ Assert.assertEquals("Unexpected input ", IAC, read);
+ read = clientIn.read();
+ Assert.assertEquals("Unexpected input ", SB, read);
+ read = clientIn.read();
+ Assert.assertEquals("Unexpected input ", TTYPE, read);
+ read = clientIn.read();
+ Assert.assertEquals("Unexpected input ", SEND, read);
+ read = clientIn.read();
+ Assert.assertEquals("Unexpected input ", IAC, read);
+ read = clientIn.read();
+ Assert.assertEquals("Unexpected input ", SE, read);
+
+ scanner.scan(IAC);
+ scanner.scan(SB);
+ scanner.scan(TTYPE);
+ scanner.scan(IS);
+ scanner.scan('A');
+ scanner.scan('B');
+ scanner.scan('C');
+ scanner.scan('D');
+ scanner.scan('E');
+ scanner.scan(IAC);
+ scanner.scan(SE);
+
+ read = clientIn.read();
+ Assert.assertEquals("Unexpected input ", IAC, read);
+ read = clientIn.read();
+ Assert.assertEquals("Unexpected input ", SB, read);
+ read = clientIn.read();
+ Assert.assertEquals("Unexpected input ", TTYPE, read);
+ read = clientIn.read();
+ Assert.assertEquals("Unexpected input ", SEND, read);
+ read = clientIn.read();
+ Assert.assertEquals("Unexpected input ", IAC, read);
+ read = clientIn.read();
+ Assert.assertEquals("Unexpected input ", SE, read);
+
+ scanner.scan(IAC);
+ scanner.scan(SB);
+ scanner.scan(TTYPE);
+ scanner.scan(IS);
+ for (byte symbol : terminalType) {
+ scanner.scan(symbol);
+ }
+ scanner.scan(IAC);
+ scanner.scan(SE);
+
+ Assert.assertEquals("Incorrect BACKSPACE: ", mappings.getBackspace(), scanner.getBackspace());
+ Assert.assertEquals("Incorrect DELL: ", mappings.getDel(), scanner.getDel());
+
+ Map<String, KEYS> currentEscapesToKey = scanner.getCurrentEscapesToKey();
+ Map<String, KEYS> expectedEscapesToKey = mappings.getEscapesToKey();
+ for (String escape : expectedEscapesToKey.keySet()) {
+ KEYS key = expectedEscapesToKey.get(escape);
+ Assert.assertEquals("Incorrect " + key.name(), key, currentEscapesToKey.get(escape));
+ }
+
+ Assert.assertTrue("Callback not called ", testCallback.getState());
+ }
+
+ class TestCallback implements Callback {
+
+ private boolean isCalled = false;
+
+ @Override
+ public void finished() {
+ isCalled = true;
+ }
+
+ public boolean getState() {
+ return isCalled;
+ }
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetOutputStreamTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetOutputStreamTests.java
new file mode 100755
index 000000000..f975f4dc7
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetOutputStreamTests.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 contribution
+ ******************************************************************************/
+
+package org.eclipse.equinox.console.telnet;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+
+public class TelnetOutputStreamTests {
+
+ @Test
+ public void testAutoSend() throws Exception {
+ ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+ TelnetOutputStream out = new TelnetOutputStream(byteOut);
+ out.autoSend();
+ out.flush();
+ byte[] message = byteOut.toByteArray();
+
+ Assert.assertNotNull("Auto message not sent", message);
+ Assert.assertFalse("Auto message not sent", message.length == 0);
+ Assert.assertTrue("Error sending auto message. Expected length: " + TelnetOutputStream.autoMessage.length + ", actual length: "
+ + message.length, message.length == TelnetOutputStream.autoMessage.length);
+
+ for (int i = 0; i < message.length; i++) {
+ Assert.assertEquals("Wrong char in auto message. Position: " + i + ", expected: " + TelnetOutputStream.autoMessage[i] + ", read: "
+ + message[i], TelnetOutputStream.autoMessage[i], message[i]);
+ }
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetServerTests.java b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetServerTests.java
new file mode 100755
index 000000000..21eb4c546
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console.tests/src/org/eclipse/equinox/console/telnet/TelnetServerTests.java
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * 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 contribution
+ ******************************************************************************/
+
+package org.eclipse.equinox.console.telnet;
+
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.CommandSession;
+import org.easymock.EasyMock;
+import org.eclipse.equinox.console.common.ConsoleInputStream;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.net.ConnectException;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.easymock.EasyMock.*;
+
+public class TelnetServerTests {
+
+ private static final String HOST = "localhost";
+ private static final int PORT = 38888;
+ private static final long WAIT_TIME = 5000;
+ private static final int TEST_CONTENT = 100;
+
+ @Test
+ public void testTelnetServer() throws Exception {
+
+ CommandSession session = EasyMock.createMock(CommandSession.class);
+ session.put((String)EasyMock.anyObject(), EasyMock.anyObject());
+ EasyMock.expectLastCall().times(3);
+ EasyMock.expect(session.execute((String)EasyMock.anyObject())).andReturn(new Object());
+ session.close();
+ EasyMock.expectLastCall();
+ EasyMock.replay(session);
+
+ CommandProcessor processor = EasyMock.createMock(CommandProcessor.class);
+ EasyMock.expect(processor.createSession((ConsoleInputStream)EasyMock.anyObject(), (PrintStream)EasyMock.anyObject(), (PrintStream)EasyMock.anyObject())).andReturn(session);
+ EasyMock.replay(processor);
+
+ List<CommandProcessor> processors = new ArrayList<CommandProcessor>();
+ processors.add(processor);
+ TelnetServer telnetServer = new TelnetServer(null, processors, HOST, PORT);
+ telnetServer.start();
+ Socket socketClient = null;
+
+ try {
+ socketClient = new Socket("localhost", PORT);
+ OutputStream outClient = socketClient.getOutputStream();
+ outClient.write(TEST_CONTENT);
+ outClient.write('\n');
+ outClient.flush();
+ // wait for the accept thread to finish execution
+ try {
+ Thread.sleep(WAIT_TIME);
+ } catch (InterruptedException ie) {
+ // do nothing
+ }
+ verify();
+ } catch(ConnectException e) {
+ Assert.fail("Telnet port not open");
+ } finally {
+ if (socketClient != null) {
+ socketClient.close();
+ }
+ telnetServer.stopTelnetServer();
+ }
+ }
+
+ @Test
+ public void testTelnetServerWithoutHost() throws Exception {
+ CommandSession session = EasyMock.createMock(CommandSession.class);
+ session.put((String)EasyMock.anyObject(), EasyMock.anyObject());
+ EasyMock.expectLastCall().times(4);
+ EasyMock.expect(session.execute((String)EasyMock.anyObject())).andReturn(new Object());
+ session.close();
+ EasyMock.expectLastCall();
+ EasyMock.replay(session);
+
+ CommandProcessor processor = EasyMock.createMock(CommandProcessor.class);
+ EasyMock.expect(processor.createSession((ConsoleInputStream)EasyMock.anyObject(), (PrintStream)EasyMock.anyObject(), (PrintStream)EasyMock.anyObject())).andReturn(session);
+ EasyMock.replay(processor);
+
+ List<CommandProcessor> processors = new ArrayList<CommandProcessor>();
+ processors.add(processor);
+ TelnetServer telnetServer = new TelnetServer(null, processors, null, PORT);
+ telnetServer.start();
+ Socket socketClient = null;
+
+ try {
+ socketClient = new Socket("localhost", PORT);
+ OutputStream outClient = socketClient.getOutputStream();
+ outClient.write(TEST_CONTENT);
+ outClient.write('\n');
+ outClient.flush();
+
+ // wait for the accept thread to finish execution
+ try {
+ Thread.sleep(WAIT_TIME);
+ } catch (InterruptedException ie) {
+ // do nothing
+ }
+ } catch(ConnectException e) {
+ Assert.fail("Telnet port not open");
+ } finally {
+ if (socketClient != null) {
+ socketClient.close();
+ }
+ telnetServer.stopTelnetServer();
+ }
+
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.console/.classpath b/bundles/org.eclipse.equinox.console/.classpath
new file mode 100755
index 000000000..64c5e31b7
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.equinox.console/.project b/bundles/org.eclipse.equinox.console/.project
new file mode 100755
index 000000000..f5b6451f7
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.equinox.console</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/bundles/org.eclipse.equinox.console/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.equinox.console/.settings/org.eclipse.jdt.core.prefs
new file mode 100755
index 000000000..0c40437fb
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+#Thu Jun 24 08:40:05 CDT 2010
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/bundles/org.eclipse.equinox.console/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.console/META-INF/MANIFEST.MF
new file mode 100755
index 000000000..d744dd60b
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/META-INF/MANIFEST.MF
@@ -0,0 +1,33 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Console supportability plug-in
+Bundle-SymbolicName: org.eclipse.equinox.console
+Bundle-Version: 1.0.0.qualifier
+Bundle-Activator: org.eclipse.equinox.console.command.adapter.Activator
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+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.service.environment,
+ org.eclipse.osgi.service.resolver,
+ org.eclipse.osgi.util,
+ org.osgi.framework,
+ org.osgi.service.cm;resolution:=optional,
+ org.osgi.service.condpermadmin,
+ org.osgi.service.packageadmin,
+ org.osgi.service.permissionadmin,
+ org.osgi.service.startlevel,
+ org.osgi.util.tracker
+Export-Package: org.eclipse.equinox.console.completion.common,
+ org.eclipse.equinox.console.jaas
diff --git a/bundles/org.eclipse.equinox.console/about.html b/bundles/org.eclipse.equinox.console/about.html
new file mode 100755
index 000000000..359fab5a4
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/about.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+
+<p>January 10, 2011</p>
+<h3>License</h3>
+
+<p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 (&quot;EPL&quot;). A copy of the EPL is available
+at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is
+being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may
+apply to your use of any object code in the Content. Check the Redistributor's license that was
+provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p>
+
+</body>
+</html> \ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.console/build.properties b/bundles/org.eclipse.equinox.console/build.properties
new file mode 100755
index 000000000..929ee1696
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/build.properties
@@ -0,0 +1,5 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ about.html,\
+ .
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/command/adapter/Activator.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/command/adapter/Activator.java
new file mode 100755
index 000000000..d6a4b4cb2
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/command/adapter/Activator.java
@@ -0,0 +1,344 @@
+/*******************************************************************************
+ * 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Thomas Watson, IBM Corporation - initial API and implementation
+ * Lazar Kirchev, SAP AG - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.equinox.console.command.adapter;
+
+import java.io.PrintStream;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.CommandSession;
+import org.eclipse.equinox.console.commands.DisconnectCommand;
+import org.eclipse.equinox.console.commands.EquinoxCommandProvider;
+import org.eclipse.equinox.console.commands.HelpCommand;
+import org.eclipse.equinox.console.commands.ManCommand;
+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;
+import org.eclipse.osgi.framework.console.ConsoleSession;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+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;
+
+/**
+ * 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, PackageAdmin> packageAdminTracker;
+ private ServiceTracker<PlatformAdmin, ?> platformAdminTracker;
+ private static boolean isFirstProcessor = true;
+ private static TelnetCommand telnetConnection = null;
+ private static SshCommand sshConnection = null;
+ private static Object telnetLock = new Object();
+ private static Object sshLock = new Object();
+ private static List<TelnetCommand> telnetConnections = new ArrayList<TelnetCommand>();
+ private static List<SshCommand> sshConnections = new ArrayList<SshCommand>();
+
+ private ServiceTracker<CommandProcessor, ServiceTracker<ConsoleSession, CommandSession>> commandProcessorTracker;
+ // Tracker for Equinox CommandProviders
+ private ServiceTracker<CommandProvider, List<ServiceRegistration<?>>> commandProviderTracker;
+
+ private EquinoxCommandProvider equinoxCmdProvider;
+
+ public static class ProcessorCustomizer implements
+ ServiceTrackerCustomizer<CommandProcessor, ServiceTracker<ConsoleSession, CommandSession>> {
+
+ private final BundleContext context;
+
+ public ProcessorCustomizer(BundleContext context) {
+ this.context = context;
+ }
+
+ public ServiceTracker<ConsoleSession, CommandSession> addingService(
+ ServiceReference<CommandProcessor> reference) {
+ CommandProcessor processor = context.getService(reference);
+ if (processor == null)
+ return null;
+
+ if (isFirstProcessor) {
+ isFirstProcessor = false;
+ telnetConnection = new TelnetCommand(processor, context);
+ telnetConnection.start();
+ sshConnection = new SshCommand(processor, context);
+ sshConnection.start();
+ } else {
+ telnetConnection.addCommandProcessor(processor);
+ sshConnection.addCommandProcessor(processor);
+ }
+
+ ServiceTracker<ConsoleSession, CommandSession> tracker = new ServiceTracker<ConsoleSession, CommandSession>(context, ConsoleSession.class, new SessionCustomizer(context, processor));
+ tracker.open();
+ return tracker;
+ }
+
+ public void modifiedService(
+ ServiceReference<CommandProcessor> reference,
+ ServiceTracker<ConsoleSession, CommandSession> service) {
+ // nothing
+ }
+
+ public void removedService(
+ ServiceReference<CommandProcessor> reference,
+ ServiceTracker<ConsoleSession, CommandSession> tracker) {
+ tracker.close();
+ CommandProcessor processor = context.getService(reference);
+ telnetConnection.removeCommandProcessor(processor);
+ sshConnection.removeCommandProcessor(processor);
+ }
+ }
+
+ // Provides support for Equinox ConsoleSessions
+ public static class SessionCustomizer implements
+ ServiceTrackerCustomizer<ConsoleSession, CommandSession> {
+ private final BundleContext context;
+ final CommandProcessor processor;
+
+ public SessionCustomizer(BundleContext context, CommandProcessor processor) {
+ this.context = context;
+ this.processor = processor;
+ }
+
+ public CommandSession addingService(
+ ServiceReference<ConsoleSession> reference) {
+ final ConsoleSession equinoxSession = context.getService(reference);
+ if (equinoxSession == null)
+ return null;
+ PrintStream output = new PrintStream(equinoxSession.getOutput());
+ final CommandSession gogoSession = processor.createSession(equinoxSession.getInput(), output, output);
+ new Thread(new Runnable(){
+ public void run() {
+ try {
+ gogoSession.put("SCOPE", "equinox:*");
+ gogoSession.put("prompt", "osgi> ");
+ gogoSession.execute("gosh --login --noshutdown");
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ }
+ finally {
+ gogoSession.close();
+ equinoxSession.close();
+ }
+ }
+
+ }, "Equinox Console Session").start();
+ return null;
+ }
+
+ public void modifiedService(ServiceReference<ConsoleSession> reference,
+ CommandSession service) {
+ // nothing
+ }
+
+ public void removedService(ServiceReference<ConsoleSession> reference,
+ CommandSession session) {
+ session.close();
+ }
+ }
+
+ // All commands, provided by an Equinox CommandProvider, are registered as provided by a CommandProviderAdapter.
+ public class CommandCustomizer implements
+ ServiceTrackerCustomizer<CommandProvider, List<ServiceRegistration<?>>> {
+
+ private BundleContext context;
+ public CommandCustomizer(BundleContext context) {
+ this.context = context;
+ }
+
+ public List<ServiceRegistration<?>> 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);
+ try {
+ Method[] commandMethods = getCommandMethods(command);
+
+ if (commandMethods.length > 0) {
+ List<ServiceRegistration<?>> registrations = new ArrayList<ServiceRegistration<?>>();
+ registrations.add(context.registerService(Object.class, new CommandProviderAdapter((CommandProvider) command, commandMethods), getAttributes(commandMethods)));
+ return registrations;
+ } else {
+ context.ungetService(reference);
+ return null;
+ }
+ } catch (Exception e) {
+ context.ungetService(reference);
+ return null;
+ }
+ }
+
+
+ public void modifiedService(ServiceReference<CommandProvider> reference, List<ServiceRegistration<?>> service) {
+ // Nothing to do.
+ }
+
+ public void removedService(ServiceReference<CommandProvider> reference, List<ServiceRegistration<?>> registrations) {
+ for (ServiceRegistration<?> serviceRegistration : registrations) {
+ serviceRegistration.unregister();
+ }
+ }
+
+ }
+
+ public void start(BundleContext context) throws Exception {
+ commandProviderTracker = new ServiceTracker<CommandProvider, List<ServiceRegistration<?>>>(context, CommandProvider.class.getName(), new CommandCustomizer(context));
+ 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, PackageAdmin>(context, PackageAdmin.class, 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();
+
+ ManCommand manCommand = new ManCommand(context);
+ manCommand.start();
+
+ DisconnectCommand disconnectCommand = new DisconnectCommand(context);
+ disconnectCommand.start();
+
+ startBundle("org.apache.felix.gogo.runtime", true);
+ startBundle("org.apache.felix.gogo.shell", true);
+ startBundle("org.apache.felix.gogo.command", false);
+ }
+
+ private void startBundle(String bsn, boolean required) throws BundleException {
+ PackageAdmin pa = packageAdminTracker.getService();
+ if (pa != null) {
+ @SuppressWarnings("deprecation")
+ Bundle[] shells = pa.getBundles(bsn, null);
+ if (shells != null && shells.length > 0) {
+ shells[0].start(Bundle.START_TRANSIENT);
+ } else if (required) {
+ throw new BundleException("Missing required bundle: " + bsn);
+ }
+ }
+ }
+
+ 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) {
+ ArrayList<Method> names = new ArrayList<Method>();
+ Class<?> c = command.getClass();
+ Method[] methods = c.getDeclaredMethods();
+ for (Method method : methods) {
+ if (method.getName().startsWith("_")
+ && method.getModifiers() == Modifier.PUBLIC && !method.getName().equals("_help")) {
+ Type[] types = method.getGenericParameterTypes();
+ if (types.length == 1
+ && types[0].equals(CommandInterpreter.class)) {
+ names.add(method);
+ }
+ }
+ }
+ return names.toArray(new Method[names.size()]);
+ }
+
+ Dictionary<String, Object> getAttributes(Method[] commandMethods) {
+ Dictionary<String, Object> dict = new Hashtable<String, Object>();
+ dict.put("osgi.command.scope", "equinox");
+ String[] methodNames = new String[commandMethods.length];
+ for (int i = 0; i < commandMethods.length; i++) {
+ String methodName = "" + commandMethods[i].getName().substring(1);
+ methodNames[i] = methodName;
+ }
+
+ dict.put("osgi.command.function", methodNames);
+ return dict;
+ }
+
+ public void stop(BundleContext context) throws Exception {
+ commandProviderTracker.close();
+ commandProcessorTracker.close();
+ if (equinoxCmdProvider != null) {
+ equinoxCmdProvider.stop();
+ }
+
+ try {
+ telnetConnection.telnet(new String[]{"stop"});
+ } catch (Exception e) {
+ // expected if the telnet server is not started
+ }
+
+ try {
+ sshConnection.ssh(new String[]{"stop"});
+ } catch (Exception e) {
+ // expected if the ssh server is not started
+ }
+
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/command/adapter/CommandProviderAdapter.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/command/adapter/CommandProviderAdapter.java
new file mode 100755
index 000000000..08de8e80c
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/command/adapter/CommandProviderAdapter.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Thomas Watson, IBM Corporation - initial API and implementation
+ * Lazar Kirchev, SAP AG - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.equinox.console.command.adapter;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+
+import org.eclipse.osgi.framework.console.CommandProvider;
+
+/**
+ * This adapter class provides for execution of legacy Equinox commands from
+ * the Gogo shell. The commands are executed through the main method of the
+ * adapter. It finds the appropriate Equinox command and executes
+ * it with the proper argument.
+ *
+ */
+public class CommandProviderAdapter {
+
+ private final CommandProvider commandProvider;
+ private final Method[] commands;
+
+
+ public CommandProviderAdapter(CommandProvider commandProvider, Method[] commands) {
+ this.commandProvider = commandProvider;
+ this.commands = commands;
+ }
+
+ public Object main(Object[] args) throws Exception {
+ try {
+ // first argument is the command
+ Method command = findCommand("_" + args[0]);
+ ArrayList<Object> argList = new ArrayList<Object>();
+ for (int i = 1; i < args.length; i++)
+ argList.add(args[i]);
+ return command.invoke(commandProvider, new CustomCommandInterpreter(argList));
+ } catch (InvocationTargetException e) {
+ if (e.getTargetException() instanceof Exception)
+ throw (Exception) e.getTargetException();
+ throw (Error) e.getTargetException();
+ }
+ }
+
+ private Method findCommand(Object commandName) {
+ for (Method command : commands) {
+ if (command.getName().equals(commandName))
+ return command;
+ }
+ throw new IllegalArgumentException("Cannot find the command method for: " + commandName);
+ }
+
+ // TODO Felix gogo seems to search for _main
+ public Object _main(Object[] args) throws Exception {
+ return main(args);
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/command/adapter/CustomCommandInterpreter.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/command/adapter/CustomCommandInterpreter.java
new file mode 100755
index 000000000..c401e361b
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/command/adapter/CustomCommandInterpreter.java
@@ -0,0 +1,258 @@
+/*******************************************************************************
+ * 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 from FrameworkCommandInterpreter
+ *******************************************************************************/
+package org.eclipse.equinox.console.command.adapter;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.net.URL;
+import java.util.*;
+import org.eclipse.osgi.framework.console.CommandInterpreter;
+import org.osgi.framework.Bundle;
+
+/**
+ * A CommandInterpreter to be passed to the legacy Equinox commands,
+ * executed by the CommandProviderAdapter.
+ *
+ */
+public class CustomCommandInterpreter implements CommandInterpreter {
+ private PrintStream out = System.out;
+ /** Strings used to format other strings */
+ private String tab = "\t"; //$NON-NLS-1$
+ private String newline = "\r\n"; //$NON-NLS-1$
+ private final Iterator<Object> arguments;
+ /**
+ * The maximum number of lines to print without user prompt.
+ * 0 means no user prompt is required, the window is scrollable.
+ */
+ protected static int maxLineCount;
+
+ /** The number of lines printed without user prompt.*/
+ protected int currentLineCount;
+
+ public CustomCommandInterpreter(List<Object> args) {
+ arguments = args.iterator();
+ }
+
+ public Object execute(String cmd) {
+ return null;
+ }
+
+ public String nextArgument() {
+ if (arguments.hasNext()) {
+ Object next = arguments.next();
+ return next == null ? null : next.toString();
+ }
+ return null;
+ }
+
+ /**
+ * Prints an object to the outputstream
+ *
+ * @param o the object to be printed
+ */
+ public void print(Object o) {
+ check4More();
+ out.print(o);
+ out.flush();
+ }
+
+ /**
+ * Prints a empty line to the outputstream
+ */
+ public void println() {
+ println(""); //$NON-NLS-1$
+ }
+
+ /**
+ * Print a stack trace including nested exceptions.
+ * @param t The offending exception
+ */
+ public void printStackTrace(Throwable t) {
+ t.printStackTrace(out);
+
+ Method[] methods = t.getClass().getMethods();
+
+ int size = methods.length;
+ Class<Throwable> throwable = Throwable.class;
+
+ for (int i = 0; i < size; i++) {
+ Method method = methods[i];
+
+ if (Modifier.isPublic(method.getModifiers()) && method.getName().startsWith("get") && throwable.isAssignableFrom(method.getReturnType()) && (method.getParameterTypes().length == 0)) { //$NON-NLS-1$
+ try {
+ Throwable nested = (Throwable) method.invoke(t, (Object) null);
+
+ if ((nested != null) && (nested != t)) {
+ out.println("Nested Exception");
+ printStackTrace(nested);
+ }
+ } catch (IllegalAccessException e) {
+ } catch (InvocationTargetException e) {
+ }
+ }
+ }
+ }
+
+ /**
+ * Prints an object to the output medium (appended with newline character).
+ * <p>
+ * If running on the target environment, the user is prompted with '--more'
+ * if more than the configured number of lines have been printed without user prompt.
+ * This enables the user of the program to have control over scrolling.
+ * <p>
+ * For this to work properly you should not embed "\n" etc. into the string.
+ *
+ * @param o the object to be printed
+ */
+ public void println(Object o) {
+ if (o == null) {
+ return;
+ }
+ synchronized (out) {
+ check4More();
+ printline(o);
+ currentLineCount++;
+ currentLineCount += o.toString().length() / 80;
+ }
+ }
+
+ /**
+ * Prints a string to the output medium (appended with newline character).
+ * <p>
+ * This method does not increment the line counter for the 'more' prompt.
+ *
+ * @param o the string to be printed
+ */
+ private void printline(Object o) {
+ print(o + newline);
+ }
+
+ /**
+ * Prints the given dictionary sorted by keys.
+ *
+ * @param dic the dictionary to print
+ * @param title the header to print above the key/value pairs
+ */
+ public void printDictionary(Dictionary<?,?> dic, String title) {
+ if (dic == null)
+ return;
+
+ int count = dic.size();
+ String[] keys = new String[count];
+ Enumeration<?> keysEnum = dic.keys();
+ int i = 0;
+ while (keysEnum.hasMoreElements()) {
+ keys[i++] = (String) keysEnum.nextElement();
+ }
+ Arrays.sort(keys);
+
+ if (title != null) {
+ println(title);
+ }
+ for (i = 0; i < count; i++) {
+ println(" " + keys[i] + " = " + dic.get(keys[i])); //$NON-NLS-1$//$NON-NLS-2$
+ }
+ println();
+ }
+
+ /**
+ * Prints the given bundle resource if it exists
+ *
+ * @param bundle the bundle containing the resource
+ * @param resource the resource to print
+ */
+ public void printBundleResource(Bundle bundle, String resource) {
+ URL entry = null;
+ entry = bundle.getEntry(resource);
+ if (entry != null) {
+ try {
+ println(resource);
+ InputStream in = entry.openStream();
+ byte[] buffer = new byte[1024];
+ int read = 0;
+ try {
+ while ((read = in.read(buffer)) != -1)
+ print(new String(buffer, 0, read));
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ } catch (Exception e) {
+ System.err.println(e);
+ }
+ } else {
+ println("CONSOLE_RESOURCE ["+resource+"] NOT_IN_BUNDLE " + bundle.toString());
+ }
+ }
+
+ /**
+ * Answers the number of lines output to the console
+ * window should scroll without user interaction.
+ *
+ * @return The number of lines to scroll.
+ */
+ private int getMaximumLinesToScroll() {
+ return maxLineCount;
+ }
+
+ /**
+ * Displays the more... prompt if the max line count has been reached
+ * and waits for the operator to hit enter.
+ *
+ */
+ private void check4More() {
+ int max = getMaximumLinesToScroll();
+ if (max > 0) {
+ if (currentLineCount >= max) {
+ out.print("-- More...Press Enter to Continue...");
+ out.flush();
+ try {
+ System.in.read();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } // wait for user entry
+ resetLineCount(); //Reset the line counter for the 'more' prompt
+ }
+ }
+ }
+
+ /**
+ * Resets the line counter for the 'more' prompt.
+ */
+ private void resetLineCount() {
+ currentLineCount = 0;
+ }
+
+ /**
+ Answer a string (may be as many lines as you like) with help
+ texts that explain the command.
+ */
+ public String getHelp() {
+ StringBuffer help = new StringBuffer(256);
+ help.append("---Controlling the Console---");
+ help.append(newline);
+ help.append(tab);
+ help.append("more - "); //$NON-NLS-1$
+ help.append("More prompt for console output");
+ help.append(newline);
+ help.append(tab);
+ help.append("disconnect - "); //$NON-NLS-1$
+ help.append("isconnects from telnet session");
+ help.append(newline);
+ return help.toString();
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/ConsoleMessages.properties b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/ConsoleMessages.properties
new file mode 100755
index 000000000..99c7f4298
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/ConsoleMessages.properties
@@ -0,0 +1,78 @@
+###############################################################################
+# 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_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/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/ConsoleMsg.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/ConsoleMsg.java
new file mode 100755
index 000000000..e58b82df6
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/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/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/DisconnectCommand.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/DisconnectCommand.java
new file mode 100755
index 000000000..69acc779c
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/DisconnectCommand.java
@@ -0,0 +1,63 @@
+package org.eclipse.equinox.console.commands;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.CommandSession;
+import java.io.Closeable;
+import org.osgi.framework.BundleContext;
+
+/**
+ * This class implements functionality to disconnect from telnet or ssh console.
+ */
+public class DisconnectCommand {
+ private static final String CLOSEABLE = "CLOSEABLE";
+ private static final String DISCONNECT_MESSAGE = "Disconnect from console? (y/n; default=y) ";
+ private static final String DISCONNECT_CONFIRMATION_Y = "y";
+
+ private BundleContext context;
+
+ public DisconnectCommand(BundleContext context) {
+ this.context = context;
+ }
+
+ public void start() {
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ props.put(CommandProcessor.COMMAND_SCOPE, "equinox");
+ props.put(CommandProcessor.COMMAND_FUNCTION, new String[] {"disconnect"});
+ context.registerService(DisconnectCommand.class.getName(), this, props);
+ }
+
+ public void disconnect(CommandSession session) {
+ System.out.print(DISCONNECT_MESSAGE);
+ System.out.flush();
+
+ BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
+ String reply = null;
+ try {
+ reply = reader.readLine();
+ } catch (IOException e) {
+ System.out.println("Error while reading confirmation");
+ }
+
+ if (reply != null) {
+ if (reply.toLowerCase().startsWith(DISCONNECT_CONFIRMATION_Y) || reply.length() == 0) {
+ Closeable closable = (Closeable)session.get(CLOSEABLE);
+ if (closable != null) {
+ try {
+ closable.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return;
+ }
+
+ session.close();
+ }
+ }
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/EquinoxCommandProvider.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/EquinoxCommandProvider.java
new file mode 100755
index 000000000..b6d452751
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/EquinoxCommandProvider.java
@@ -0,0 +1,1819 @@
+/*******************************************************************************
+ * 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.lang.reflect.Method;
+import java.net.URL;
+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.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;
+ }
+ }
+
+ /**
+ * 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
+ */
+ @SuppressWarnings("deprecation")
+ @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
+ **/
+ @SuppressWarnings("deprecation")
+ @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);
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ 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
+ */
+ @SuppressWarnings({ "deprecation" })
+ @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);
+ }
+ }
+ }
+
+ 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
+ */
+ @SuppressWarnings("deprecation")
+ @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(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<EnvironmentInfo> envInfoRef = context.getServiceReference(EnvironmentInfo.class);
+ 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
+ */
+ @SuppressWarnings("deprecation")
+ @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
+ */
+ @SuppressWarnings("deprecation")
+ @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
+ */
+ @SuppressWarnings("deprecation")
+ @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
+ */
+ @SuppressWarnings("deprecation")
+ @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
+ */
+ @SuppressWarnings("deprecation")
+ @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
+ */
+ @SuppressWarnings("deprecation")
+ @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 {
+ Class<?> profileClass = BundleContext.class.getClassLoader().loadClass("org.eclipse.osgi.internal.profile.Profile");
+ Method getProfileLog = profileClass.getMethod("getProfileLog", (Class<?>[]) null);
+ System.out.println(getProfileLog.invoke(null, (Object[]) null));
+ }
+
+ /**
+ * 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/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/EquinoxCommandsConverter.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/EquinoxCommandsConverter.java
new file mode 100755
index 000000000..76ce91115
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/EquinoxCommandsConverter.java
@@ -0,0 +1,173 @@
+/*******************************************************************************
+ * 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.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<?>) {
+ List<?> args = (List<?>) in;
+ if (checkStringElements(args)) {
+ try {
+ ArrayList<Bundle> bundles = new ArrayList<Bundle>();
+ for (Object arg : args) {
+ long id = Long.parseLong((String)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;
+ }
+
+ private boolean checkStringElements(List<?> list) {
+ for (Object element : list) {
+ if (!(element instanceof String)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ 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<?>) {
+ List<?> list = (List<?>) target;
+ if (checkDictionaryElements(list)) {
+ StringBuilder builder = new StringBuilder();
+ for(Object dic : list) {
+ builder.append("Bundle headers:\r\n");
+ builder.append(printDictionary((Dictionary<?, ?>)dic));
+ builder.append("\r\n");
+ builder.append("\r\n");
+ }
+ return builder.toString();
+ }
+ }
+
+ return null;
+ }
+
+ private boolean checkDictionaryElements(List<?> list) {
+ for (Object element : list) {
+ if (!(element instanceof Dictionary<?, ?>)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ 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/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/HelpCommand.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/HelpCommand.java
new file mode 100755
index 000000000..f034dc4a1
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/HelpCommand.java
@@ -0,0 +1,186 @@
+/*******************************************************************************
+ * 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.eclipse.equinox.console.command.adapter.CustomCommandInterpreter;
+import org.eclipse.osgi.framework.console.CommandInterpreter;
+import org.eclipse.osgi.framework.console.CommandProvider;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+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;
+
+ 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. 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.
+ *
+ * This method can accept an additional argument -all. If this option is specified, then both the names of the
+ * legacy equinox commands and the Gogo commands are displayed.
+ *
+ * @param session
+ * @param args
+ * @throws Exception
+ */
+ public void help(final CommandSession session, String... args) throws Exception {
+ String command = null;
+
+ if (args.length > 0) {
+ command = args[0];
+ }
+
+ if (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;
+ }
+ }
+ }
+
+ try {
+ session.execute("felix:help " + command);
+ } catch (IllegalArgumentException e) {
+ handleCommandNotFound();
+ }
+
+ return;
+ }
+
+ printLegacyCommands();
+ try {
+ session.execute("felix:help");
+ } catch (IllegalArgumentException e) {
+ handleCommandNotFound();
+ }
+
+ }
+
+ private void printLegacyCommands() {
+ for (CommandProvider provider : legacyCommandProviders) {
+ Method[] methods = provider.getClass().getMethods();
+ for (Method method : methods) {
+ if (method.getName().startsWith("_") && !method.getName().equals("_help")) {
+ System.out.println("equinox:" + method.getName().substring(1));
+ }
+ }
+ }
+ }
+
+ private boolean checkStarted(String symbolicName) {
+ Bundle[] bundles = context.getBundles();
+ for (Bundle bundle : bundles) {
+ if (bundle.getSymbolicName().equals(symbolicName) && bundle.getState() == Bundle.ACTIVE) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private void handleCommandNotFound() {
+ if (checkStarted("org.apache.felix.gogo.command")) {
+ System.out.println("Cannot find felix:help command");
+ } else {
+ System.out.println("Cannot find felix:help command; bundle org.apache.felix.gogo.command is not started");
+ }
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/ManCommand.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/ManCommand.java
new file mode 100755
index 000000000..da5441f8c
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/ManCommand.java
@@ -0,0 +1,43 @@
+package org.eclipse.equinox.console.commands;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.CommandSession;
+import org.osgi.framework.BundleContext;
+
+public class ManCommand {
+ private BundleContext context;
+
+ public ManCommand(BundleContext context) {
+ this.context = context;
+ }
+
+ public void start() {
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ props.put(CommandProcessor.COMMAND_SCOPE, "equinox");
+ props.put(CommandProcessor.COMMAND_FUNCTION, new String[] {"man"});
+ context.registerService(ManCommand.class.getName(), this, props);
+ }
+
+ public void man(CommandSession session, String... args) throws Exception {
+ StringBuilder builder = null;
+ if (args.length > 0) {
+ builder = new StringBuilder();
+ for(String arg : args) {
+ builder.append(arg);
+ builder.append(" ");
+ }
+ }
+
+ String cmdForExecution = null;
+ if (builder != null) {
+ cmdForExecution = "equinox:help" + " " + builder.toString().trim();
+ } else {
+ cmdForExecution = "equinox:help";
+ }
+
+ session.execute(cmdForExecution);
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/Util.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/Util.java
new file mode 100755
index 000000000..bb3c19200
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/Util.java
@@ -0,0 +1,208 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.equinox.console.commands;
+
+/**
+ * This class contains utility functions.
+ */
+public class Util {
+ /**
+ * Performs a quicksort of the given objects
+ * by their string representation in ascending order.
+ * <p>
+ *
+ * @param array The array of objects to sort
+ */
+ public static void sortByString(Object[] array) {
+ qSortByString(array, 0, array.length - 1);
+ }
+
+ /**
+ * Sorts the array of objects by their string representation
+ * in ascending order.
+ * <p>
+ * This is a version of C.A.R Hoare's Quick Sort algorithm.
+ *
+ * @param array the array of objects to sort
+ * @param start the start index to begin sorting
+ * @param stop the end index to stop sorting
+ *
+ * @exception ArrayIndexOutOfBoundsException when <code>start < 0</code>
+ * or <code>end >= array.length</code>
+ */
+ public static void qSortByString(Object[] array, int start, int stop) {
+ if (start >= stop)
+ return;
+
+ int left = start; // left index
+ int right = stop; // right index
+ Object temp; // for swapping
+
+ // arbitrarily establish a partition element as the midpoint of the array
+ String mid = String.valueOf(array[(start + stop) / 2]);
+
+ // loop through the array until indices cross
+ while (left <= right) {
+ // find the first element that is smaller than the partition element from the left
+ while ((left < stop) && (String.valueOf(array[left]).compareTo(mid) < 0)) {
+ ++left;
+ }
+ // find an element that is smaller than the partition element from the right
+ while ((right > start) && (mid.compareTo(String.valueOf(array[right])) < 0)) {
+ --right;
+ }
+ // if the indices have not crossed, swap
+ if (left <= right) {
+ temp = array[left];
+ array[left] = array[right];
+ array[right] = temp;
+ ++left;
+ --right;
+ }
+ }
+ // sort the left partition, if the right index has not reached the left side of array
+ if (start < right) {
+ qSortByString(array, start, right);
+ }
+ // sort the right partition, if the left index has not reached the right side of array
+ if (left < stop) {
+ qSortByString(array, left, stop);
+ }
+ }
+
+ /**
+ * Sorts the specified range in the array in ascending order.
+ *
+ * @param array the Object array to be sorted
+ * @param start the start index to sort
+ * @param end the last + 1 index to sort
+ *
+ * @exception ClassCastException when an element in the array does not
+ * implement Comparable or elements cannot be compared to each other
+ * @exception IllegalArgumentException when <code>start > end</code>
+ * @exception ArrayIndexOutOfBoundsException when <code>start < 0</code>
+ * or <code>end > array.size()</code>
+ */
+ @SuppressWarnings("unchecked")
+ public static void sort(Object[] array, int start, int end) {
+ int middle = (start + end) / 2;
+ if (start + 1 < middle)
+ sort(array, start, middle);
+ if (middle + 1 < end)
+ sort(array, middle, end);
+ if (start + 1 >= end)
+ return; // this case can only happen when this method is called by the user
+ if (((Comparable<Object>) array[middle - 1]).compareTo(array[middle]) <= 0)
+ return;
+ if (start + 2 == end) {
+ Object temp = array[start];
+ array[start] = array[middle];
+ array[middle] = temp;
+ return;
+ }
+ int i1 = start, i2 = middle, i3 = 0;
+ Object[] merge = new Object[end - start];
+ while (i1 < middle && i2 < end) {
+ merge[i3++] = ((Comparable<Object>) array[i1]).compareTo(array[i2]) <= 0 ? array[i1++] : array[i2++];
+ }
+ if (i1 < middle)
+ System.arraycopy(array, i1, merge, i3, middle - i1);
+ System.arraycopy(merge, 0, array, start, i2 - start);
+ }
+
+ /**
+ * Sorts the specified range in the array in descending order.
+ *
+ * @param array the Object array to be sorted
+ * @param start the start index to sort
+ * @param end the last + 1 index to sort
+ *
+ * @exception ClassCastException when an element in the array does not
+ * implement Comparable or elements cannot be compared to each other
+ * @exception IllegalArgumentException when <code>start > end</code>
+ * @exception ArrayIndexOutOfBoundsException when <code>start < 0</code>
+ * or <code>end > array.size()</code>
+ */
+ public static void dsort(Object[] array, int start, int end) {
+ // first sort in ascending order
+ sort(array, start, end);
+ // then swap the elements in the array
+ swap(array);
+ }
+
+ /**
+ * Reverse the elements in the array.
+ *
+ * @param array the Object array to be reversed
+ */
+ public static void swap(Object[] array) {
+ int start = 0;
+ int end = array.length - 1;
+ while (start < end) {
+ Object temp = array[start];
+ array[start++] = array[end];
+ array[end--] = temp;
+ }
+ }
+
+ /**
+ * Returns a string representation of the object
+ * in the given length.
+ * If the string representation of the given object
+ * is longer then it is truncated.
+ * If it is shorter then it is padded with the blanks
+ * to the given total length.
+ * If the given object is a number then the padding
+ * is done on the left, otherwise on the right.
+ *
+ * @param object the object to convert
+ * @param length the length the output string
+ */
+ public static String toString(Object object, int length) {
+ boolean onLeft = object instanceof Number;
+ return toString(object, length, ' ', onLeft);
+ }
+
+ /**
+ * Returns a string representation of the object
+ * in the given length.
+ * If the string representation of the given object
+ * is longer then it is truncated.
+ * If it is shorter then it is padded to the left or right
+ * with the given character to the given total length.
+ *
+ * @param object the object to convert
+ * @param length the length the output string
+ * @param pad the pad character
+ * @param onLeft if <code>true</code> pad on the left, otherwise an the right
+ */
+ public static String toString(Object object, int length, char pad, boolean onLeft) {
+ String input = String.valueOf(object);
+ int size = input.length();
+ if (size >= length) {
+ int start = (onLeft) ? size - length : 0;
+ return input.substring(start, length);
+ }
+
+ StringBuffer padding = new StringBuffer(length - size);
+ for (int i = size; i < length; i++)
+ padding.append(pad);
+
+ StringBuffer stringBuffer = new StringBuffer(length);
+ if (onLeft)
+ stringBuffer.append(padding.toString());
+ stringBuffer.append(input);
+ if (!onLeft)
+ stringBuffer.append(padding.toString());
+ return stringBuffer.toString();
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/ConsoleInputStream.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/ConsoleInputStream.java
new file mode 100755
index 000000000..73ab4ec32
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/ConsoleInputStream.java
@@ -0,0 +1,155 @@
+/*******************************************************************************
+ * 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
+ * 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.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+
+/**
+ * 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;
+ private boolean isClosed;
+
+ public synchronized int read() {
+ while (current == null && buffer.isEmpty() && !isClosed) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ return -1;
+ }
+ }
+ if (isClosed) {
+ return -1;
+ }
+
+ try {
+ if (current == null) {
+ current = buffer.remove(0);
+ return current[pos++] & 0xFF;
+ } else {
+
+ return current[pos++] & 0xFF;
+ }
+ } finally {
+ if (current != null) {
+ if (pos == current.length) {
+ current = null;
+ pos = 0;
+ }
+ }
+ }
+
+ }
+
+ /*public int read(byte b[], int off, int len) throws IOException {
+ if (len == 0) {
+ return len;
+ }
+ int i = read();
+ if (i == -1) {
+ return -1;
+ }
+ 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 {
+ isClosed = true;
+ notifyAll();
+ }
+
+ public synchronized void add(byte[] data) {
+ if (data.length > 0) {
+ buffer.add(data);
+ notify();
+ }
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/ConsoleOutputStream.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/ConsoleOutputStream.java
new file mode 100755
index 000000000..575342c25
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/ConsoleOutputStream.java
@@ -0,0 +1,143 @@
+/*******************************************************************************
+ * Copyright (c) 2010 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.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * This class wraps the actual output stream (e.g., a socket output stream) and is responsible for
+ * buffering and flushing the characters to the actual output stream.
+ */
+public class ConsoleOutputStream extends OutputStream {
+
+ /**
+ * A size of the used buffer.
+ */
+ public static final int BUFFER_SIZE = 2048;
+ public final static byte CR = (byte) '\r';
+ public final static byte LF = (byte) '\n';
+
+ OutputStream out;
+ OutputStream oldOut;
+
+ private boolean isEcho = true;
+ private boolean queueing = false;
+ private byte prevByte;
+ private byte[] buffer;
+ private int pos;
+
+ /**
+ * Initiates with instance of the output stream to which it will send data. Here it writes to
+ * a socket output stream.
+ *
+ * @param out OutputStream for console output
+ */
+ public ConsoleOutputStream(OutputStream out) {
+ this.out = out;
+ buffer = new byte[BUFFER_SIZE];
+ pos = 0;
+ }
+
+ /**
+ * An implementation of the corresponding abstract method in OutputStream.
+ */
+ public synchronized void write(int i) throws IOException {
+
+ if (!queueing) {
+ if (isEcho) {
+ if (i == '\r' || i == '\0') {
+ queueing = true;
+ prevByte = (byte) i;
+ } else if (i == '\n') {
+ add(CR);
+ add(LF);
+ } else {
+ add(i);
+ }
+ }
+ } else { // awaiting '\n' AFTER '\r', and '\b' AFTER '\0'
+ if (prevByte == '\0' && i == '\b') {
+ isEcho = !isEcho;
+ } else if (isEcho) {
+ if (prevByte == '\r' && i == '\n') {
+ add(CR);
+ add(LF);
+ } else {
+ add(CR);
+ add(LF);
+ add(i);
+ }
+ }
+
+ queueing = false;
+ flush();
+ }
+
+ }
+
+ /**
+ * Empties the buffer and sends data to the socket output stream.
+ *
+ * @throws IOException
+ */
+ public synchronized void flush() throws IOException {
+ if (pos > 0) {
+ try {
+ out.write(buffer, 0, pos);
+ out.flush();
+ } finally {
+ pos = 0;
+ }
+
+ }
+ }
+
+ /**
+ * Adds a variable of type integer to the buffer.
+ *
+ * @param i integer to add
+ * @throws java.io.IOException if there are problems adding the integer
+ */
+ private void add(int i) throws IOException {
+ buffer[pos] = (byte) i;
+ pos++;
+
+ if (pos == buffer.length) {
+ flush();
+ }
+ }
+
+ /**
+ * Closes this OutputStream.
+ *
+ * @throws IOException
+ */
+ public void close() throws IOException {
+ out.close();
+ }
+
+ /**
+ * Substitutes the output stream. The old one is stored so that it can be restored later.
+ *
+ * @param newOut new output stream to use.
+ */
+ public void setOutput(OutputStream newOut) {
+ if (newOut != null) {
+ oldOut = out;
+ out = newOut;
+ } else {
+ out = oldOut;
+ }
+
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/InputHandler.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/InputHandler.java
new file mode 100755
index 000000000..38451ad75
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/InputHandler.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * 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
+ * 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.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * This class represents a generic handler of content, read from some input stream. It reads from the
+ * stream, and passes what is read to a processor, which performs some actions on the content,
+ * 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;
+ protected InputStream input;
+ protected byte[] buffer;
+ protected static final int MAX_SIZE = 2048;
+
+ public InputHandler(InputStream input, ConsoleInputStream in, OutputStream out) {
+ this.input = input;
+ this.in = in;
+ this.out = out;
+ buffer = new byte[MAX_SIZE];
+ }
+
+ public void run() {
+ int count;
+ try {
+ while ((count = input.read(buffer)) > -1) {
+ for (int i = 0; i < count; i++) {
+ inputScanner.scan(buffer[i]);
+ }
+ }
+ } catch (IOException e) {
+ // Printing stack trace is not needed since the streams are closed immediately
+ // do nothing
+ } finally {
+ try {
+ in.close();
+ } catch (IOException e1) {
+ // do nothing
+ }
+ try {
+ out.close();
+ } catch (IOException e1) {
+ // do nothing
+ }
+ }
+ }
+
+ public Scanner getScanner() {
+ return inputScanner;
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/KEYS.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/KEYS.java
new file mode 100755
index 000000000..87f256da6
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/KEYS.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2010 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;
+
+/**
+ * The supported escape sequences.
+ */
+public enum KEYS {
+ UP,
+ DOWN,
+ RIGHT,
+ LEFT,
+ CENTER,
+ HOME,
+ END,
+ PGUP,
+ PGDN,
+ INS,
+ DEL,
+ UNFINISHED,
+ UNKNOWN
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/Scanner.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/Scanner.java
new file mode 100755
index 000000000..08e6f077e
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/Scanner.java
@@ -0,0 +1,146 @@
+/*******************************************************************************
+ * Copyright (c) 2010 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.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+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 {
+
+ 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 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;
+ protected Map<String, KEYS> currentEscapesToKey;
+ protected final Map<String, TerminalTypeMappings> supportedEscapeSequences;
+ protected String[] escapes;
+
+ public Scanner(ConsoleInputStream toShell, OutputStream toTelnet) {
+ this.toShell = toShell;
+ this.toTelnet = toTelnet;
+ supportedEscapeSequences = new HashMap<String, TerminalTypeMappings> ();
+ supportedEscapeSequences.put("ANSI", 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());
+ }
+
+ public abstract void scan(int b) throws IOException;
+
+ public void toggleEchoEnabled(boolean isEnabled) {
+ isEchoEnabled = isEnabled;
+ }
+
+ protected void echo(int b) throws IOException {
+ if (isEchoEnabled) {
+ toTelnet.write(b);
+ }
+ }
+
+ protected void flush() throws IOException {
+ toTelnet.flush();
+ }
+
+ protected KEYS checkEscape(String possibleEsc) {
+ if (currentEscapesToKey.get(possibleEsc) != null) {
+ return currentEscapesToKey.get(possibleEsc);
+ }
+
+ for (String escape : escapes) {
+ if (escape.startsWith(possibleEsc)) {
+ return KEYS.UNFINISHED;
+ }
+ }
+ return KEYS.UNKNOWN;
+ }
+
+ protected String esc;
+ protected boolean isEsc = false;
+
+ protected void startEsc() {
+ isEsc = true;
+ esc = "";
+ }
+
+ protected abstract void scanEsc(final int b) throws IOException;
+
+ public byte getBackspace() {
+ return BACKSPACE;
+ }
+
+ public void setBackspace(byte backspace) {
+ BACKSPACE = backspace;
+ }
+
+ public byte getDel() {
+ return DEL;
+ }
+
+ public void setDel(byte del) {
+ DEL = del;
+ }
+
+ public Map<String, KEYS> getCurrentEscapesToKey() {
+ return currentEscapesToKey;
+ }
+
+ public void setCurrentEscapesToKey(Map<String, KEYS> currentEscapesToKey) {
+ this.currentEscapesToKey = currentEscapesToKey;
+ }
+
+ public String[] getEscapes() {
+ if (escapes != null) {
+ String[] copy = new String[escapes.length];
+ System.arraycopy(escapes, 0, copy, 0, escapes.length);
+ return copy;
+ } else {
+ return null;
+ }
+ }
+
+ public void setEscapes(String[] escapes) {
+ if (escapes != null) {
+ this.escapes = new String[escapes.length];
+ System.arraycopy(escapes, 0, this.escapes, 0, escapes.length);
+ } else {
+ this.escapes = null;
+ }
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/SimpleByteBuffer.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/SimpleByteBuffer.java
new file mode 100755
index 000000000..188b14bdb
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/SimpleByteBuffer.java
@@ -0,0 +1,148 @@
+/*******************************************************************************
+ * 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Lazar Kirchev, SAP AG - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.equinox.console.common;
+
+/**
+ * This is a helper class, which buffers one line of input. It provides
+ * for simple line editing - insertion, deletion, left and right movement, deletion through the
+ * backspace key.
+ */
+public class SimpleByteBuffer {
+
+ private static int INITAL_SIZE = 13;
+
+ private byte[] buffer;
+ private int pos = 0;
+ private int size = 0;
+
+ public SimpleByteBuffer() {
+ buffer = new byte[INITAL_SIZE];
+ }
+
+ public void add(final int b) {
+ if (size >= buffer.length) {
+ rezize();
+ }
+ buffer[size++] = (byte) b;
+ }
+
+ private void rezize() {
+ final byte[] newbuffeer = new byte[buffer.length << 1];
+ System.arraycopy(buffer, 0, newbuffeer, 0, buffer.length);
+ buffer = newbuffeer;
+ }
+
+ public void insert(int b) {
+ if (size >= buffer.length) {
+ rezize();
+ }
+ final int forCopy = size - pos;
+ if (forCopy > 0) {
+ System.arraycopy(buffer, pos, buffer, pos + 1, forCopy);
+ }
+ buffer[pos++] = (byte) b;
+ size++;
+ }
+
+ public int goRight() {
+ if (pos < size) {
+ return buffer[pos++] & 0xFF;
+ }
+ return -1;
+ }
+
+ public boolean goLeft() {
+ if (pos > 0) {
+ pos--;
+ return true;
+ }
+ return false;
+ }
+
+ public void delete() {
+ if (pos < size) {
+ final int forCopy = size - pos;
+ System.arraycopy(buffer, pos + 1, buffer, pos, forCopy);
+ size--;
+ }
+ }
+
+ public boolean backSpace() {
+ if (pos > 0 && size > 0) {
+ final int forCopy = size - pos;
+ System.arraycopy(buffer, pos, buffer, pos - 1, forCopy);
+ size--;
+ pos--;
+ return true;
+ }
+ return false;
+ }
+
+ public void delAll() {
+ pos = 0;
+ size = 0;
+ }
+
+ public byte[] getCurrentData() {
+ byte[] res = new byte[size];
+ System.arraycopy(buffer, 0, res, 0, size);
+ pos = 0;
+ size = 0;
+ return res;
+ }
+
+ public void set(byte[] newData) {
+ pos = 0;
+ size = 0;
+ if (newData != null) {
+ for (byte data : newData) {
+ insert(data);
+ }
+ }
+ }
+
+ public int getPos() {
+ return pos;
+ }
+
+ public byte[] copyCurrentData() {
+ byte[] res = new byte[size];
+ System.arraycopy(buffer, 0, res, 0, size);
+ return res;
+ }
+
+ public int getCurrentChar() {
+ if (pos < size) {
+ return buffer[pos] & 0xFF;
+ } else {
+ return -1;
+ }
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ public int resetPos() {
+ int res = pos;
+ pos = 0;
+ return res;
+ }
+
+ public void replace(int b) {
+ if (pos == size) {
+ insert(b);
+ } else {
+ buffer[pos++] = (byte) b;
+ }
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/terminal/ANSITerminalTypeMappings.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/terminal/ANSITerminalTypeMappings.java
new file mode 100755
index 000000000..0396c27ec
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/terminal/ANSITerminalTypeMappings.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Lazar Kirchev, SAP AG - initial API and implementation
+ *******************************************************************************/
+
+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 ANSITerminalTypeMappings() {
+ super();
+ BACKSPACE = 8;
+ DEL = 127;
+ }
+
+ public void setKeypadMappings() {
+ escapesToKey.put("[1~", KEYS.HOME); //$NON-NLS-1$
+ escapesToKey.put("[4~", KEYS.END); //$NON-NLS-1$
+ escapesToKey.put("[5~", KEYS.PGUP); //$NON-NLS-1$
+ escapesToKey.put("[6~", KEYS.PGDN); //$NON-NLS-1$
+ escapesToKey.put("[2~", KEYS.INS); //$NON-NLS-1$
+ escapesToKey.put("[3~", KEYS.DEL); //$NON-NLS-1$
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/terminal/SCOTerminalTypeMappings.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/terminal/SCOTerminalTypeMappings.java
new file mode 100755
index 000000000..7bdef7d6c
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/terminal/SCOTerminalTypeMappings.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Lazar Kirchev, SAP AG - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.equinox.console.common.terminal;
+
+import org.eclipse.equinox.console.common.KEYS;
+
+public class SCOTerminalTypeMappings extends TerminalTypeMappings {
+
+ public SCOTerminalTypeMappings() {
+ super();
+
+ BACKSPACE = -1;
+ DEL = 127;
+ }
+
+ @Override
+ public void setKeypadMappings() {
+ escapesToKey.put("[H", KEYS.HOME);
+ escapesToKey.put("F", KEYS.END);
+ escapesToKey.put("[L", KEYS.INS);
+ escapesToKey.put("[I", KEYS.PGUP);
+ escapesToKey.put("[G", KEYS.PGDN);
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/terminal/TerminalTypeMappings.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/terminal/TerminalTypeMappings.java
new file mode 100755
index 000000000..53dc2a699
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/terminal/TerminalTypeMappings.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Lazar Kirchev, SAP AG - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.equinox.console.common.terminal;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.equinox.console.common.KEYS;
+
+/**
+ * This is the base class for all supported terminal types.
+ * It contains the escape sequences, common for all mappings.
+ */
+public abstract class TerminalTypeMappings {
+ protected Map<String, KEYS> escapesToKey;
+ protected String[] escapes;
+ protected byte BACKSPACE;
+ protected byte DEL;
+
+ public TerminalTypeMappings() {
+ escapesToKey = new HashMap<String, KEYS>();
+ escapesToKey.put("[A", KEYS.UP); //$NON-NLS-1$
+ escapesToKey.put("[B", KEYS.DOWN); //$NON-NLS-1$
+ escapesToKey.put("[C", KEYS.RIGHT); //$NON-NLS-1$
+ escapesToKey.put("[D", KEYS.LEFT); //$NON-NLS-1$
+ escapesToKey.put("[G", KEYS.CENTER); //$NON-NLS-1$
+ setKeypadMappings();
+ createEscapes();
+ }
+
+ public Map<String, KEYS> getEscapesToKey() {
+ return escapesToKey;
+ }
+
+ public String[] getEscapes() {
+ if (escapes != null) {
+ String[] copy = new String[escapes.length];
+ System.arraycopy(escapes, 0, copy, 0, escapes.length);
+ return copy;
+ } else {
+ return null;
+ }
+ }
+
+ public byte getBackspace() {
+ return BACKSPACE;
+ }
+
+ public byte getDel() {
+ return DEL;
+ }
+
+ public abstract void setKeypadMappings();
+
+ private void createEscapes() {
+ escapes = new String[escapesToKey.size()];
+ Object[] temp = escapesToKey.keySet().toArray();
+ for (int i = 0; i < escapes.length; i++) {
+ escapes[i] = (String) temp[i];
+ }
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/terminal/VT100TerminalTypeMappings.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/terminal/VT100TerminalTypeMappings.java
new file mode 100755
index 000000000..19d36d431
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/terminal/VT100TerminalTypeMappings.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Lazar Kirchev, SAP AG - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.equinox.console.common.terminal;
+
+import org.eclipse.equinox.console.common.KEYS;
+
+public class VT100TerminalTypeMappings extends TerminalTypeMappings {
+
+ public VT100TerminalTypeMappings() {
+ super();
+ BACKSPACE = 127;
+ DEL = -1;
+ }
+
+ @Override
+ public void setKeypadMappings() {
+ escapesToKey.put("[H", KEYS.HOME); //$NON-NLS-1$
+ escapesToKey.put("[4~", KEYS.END); //$NON-NLS-1$
+ escapesToKey.put("[5~", KEYS.PGUP); //$NON-NLS-1$
+ escapesToKey.put("[6~", KEYS.PGDN); //$NON-NLS-1$
+ escapesToKey.put("[2~", KEYS.INS); //$NON-NLS-1$
+ escapesToKey.put("[3~", KEYS.DEL); //$NON-NLS-1$
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/terminal/VT220TerminalTypeMappings.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/terminal/VT220TerminalTypeMappings.java
new file mode 100755
index 000000000..9b832d9e8
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/terminal/VT220TerminalTypeMappings.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Lazar Kirchev, SAP AG - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.equinox.console.common.terminal;
+
+/**
+ * For the supported escape sequences, the VT220 and XTERM sequences
+ * are one and the same.
+ *
+ */
+public class VT220TerminalTypeMappings extends ANSITerminalTypeMappings {
+
+ public VT220TerminalTypeMappings() {
+ super();
+
+ BACKSPACE = 127;
+ DEL = -1;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/terminal/VT320TerminalTypeMappings.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/terminal/VT320TerminalTypeMappings.java
new file mode 100755
index 000000000..5fcf382fc
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/common/terminal/VT320TerminalTypeMappings.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Lazar Kirchev, SAP AG - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.equinox.console.common.terminal;
+
+import org.eclipse.equinox.console.common.KEYS;
+
+public class VT320TerminalTypeMappings extends TerminalTypeMappings {
+
+ public VT320TerminalTypeMappings() {
+ super();
+ BACKSPACE = 8;
+ DEL = 127;
+ }
+
+ @Override
+ public void setKeypadMappings() {
+ escapesToKey.put("[H", KEYS.HOME);
+ escapesToKey.put("[F", KEYS.END);
+ escapesToKey.put("[5~", KEYS.PGUP); //$NON-NLS-1$
+ escapesToKey.put("[6~", KEYS.PGDN); //$NON-NLS-1$
+ escapesToKey.put("[2~", KEYS.INS); //$NON-NLS-1$
+ escapesToKey.put("[3~", KEYS.DEL); //$NON-NLS-1$
+
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/completion/CommandLineParser.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/completion/CommandLineParser.java
new file mode 100755
index 000000000..ecb18fb78
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/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/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/completion/CommandNamesCompleter.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/completion/CommandNamesCompleter.java
new file mode 100755
index 000000000..5199a3360
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/completion/CommandNamesCompleter.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * 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.completion.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
+ @SuppressWarnings("unchecked")
+ 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/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/completion/CompletionHandler.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/completion/CompletionHandler.java
new file mode 100755
index 000000000..b805aeebe
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/completion/CompletionHandler.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * 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.completion.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;
+ }
+
+ @SuppressWarnings("unchecked")
+ 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/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/completion/FileNamesCompleter.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/completion/FileNamesCompleter.java
new file mode 100755
index 000000000..a69ab5e46
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/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.completion.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/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/completion/StringsCompleter.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/completion/StringsCompleter.java
new file mode 100755
index 000000000..737237925
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/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.completion.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/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/completion/VariableNamesCompleter.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/completion/VariableNamesCompleter.java
new file mode 100755
index 000000000..e72277de0
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/completion/VariableNamesCompleter.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * 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.completion.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
+ @SuppressWarnings("unchecked")
+ Set<String> variableNames = (Set<String>) session.get(null);
+ StringsCompleter completer = new StringsCompleter(variableNames, false);
+ return completer.getCandidates(buffer, cursor);
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/completion/common/Completer.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/completion/common/Completer.java
new file mode 100755
index 000000000..d63b632ed
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/completion/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.completion.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/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/jaas/RolePrincipal.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/jaas/RolePrincipal.java
new file mode 100755
index 000000000..c5f8c4ad4
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/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/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/jaas/SecureStorageLoginModule.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/jaas/SecureStorageLoginModule.java
new file mode 100755
index 000000000..941fb17e6
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/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/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/jaas/UserPrincipal.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/jaas/UserPrincipal.java
new file mode 100755
index 000000000..7af4162a1
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/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/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/ssh/SshCommand.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/ssh/SshCommand.java
new file mode 100755
index 000000000..136509a84
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/ssh/SshCommand.java
@@ -0,0 +1,306 @@
+/*******************************************************************************
+ * 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.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.Descriptor;
+import org.eclipse.equinox.console.storage.DigestUtil;
+import org.eclipse.equinox.console.storage.SecureUserStore;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+
+/**
+ * This class implements a command for starting/stopping a simple ssh server.
+ *
+ */
+public class SshCommand {
+ private String defaultHost = null;
+ private int defaultPort;
+ private List<CommandProcessor> processors = new ArrayList<CommandProcessor>();
+ private String host = null;
+ private int port;
+ private SshServ sshServ;
+ private BundleContext context;
+ private ServiceRegistration<?> configuratorRegistration;
+ private boolean isEnabled = false;
+ private final Object lock = new Object();
+
+ 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";
+ private static final String HOST = "host";
+ private static final String PORT = "port";
+ private static final String USE_CONFIG_ADMIN_PROP = "osgi.console.useConfigAdmin";
+ private static final String SSH_PID = "osgi.console.ssh";
+ private static final String ENABLED = "enabled";
+
+ public SshCommand(CommandProcessor processor, BundleContext context) {
+ processors.add(processor);
+ this.context = context;
+
+ if ("true".equals(context.getProperty(USE_CONFIG_ADMIN_PROP))) {
+ Dictionary<String, String> sshProperties = new Hashtable<String, String>();
+ sshProperties.put(Constants.SERVICE_PID, SSH_PID);
+ try {
+ synchronized (lock) {
+ configuratorRegistration = context.registerService(ManagedService.class.getName(), new SshConfigurator(), sshProperties);
+ }
+ } catch (NoClassDefFoundError e) {
+ System.out.println("Configuration Admin not available!");
+ return;
+ }
+ } else {
+ parseHostAndPort();
+ }
+ }
+
+ private void parseHostAndPort() {
+ String sshPort = null;
+ String consolePropValue = context.getProperty(SSH_PID);
+ if(consolePropValue != null) {
+ int index = consolePropValue.lastIndexOf(":");
+ if (index > -1) {
+ defaultHost = consolePropValue.substring(0, index);
+ }
+ sshPort = consolePropValue.substring(index + 1);
+ isEnabled = true;
+ }
+ 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) && isEnabled == true) {
+ 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(processors, context, host, port);
+ } catch (NoClassDefFoundError e) {
+ // ssh server bundles are optional and may not be available
+ System.out.println("SSH bundles not available! If you want to use SSH, please install Apache sshd-core, Apache mina-core, slf4j-api and a slf4j logger implementation bundles");
+ 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;
+ }
+ }
+
+ public synchronized void addCommandProcessor(CommandProcessor processor) {
+ processors.add(processor);
+ sshServ.addCommandProcessor(processor);
+ }
+
+ public synchronized void removeCommandProcessor(CommandProcessor processor) {
+ processors.remove(processor);
+ sshServ.removeCommandProcessor(processor);
+ }
+
+ private void checkPortAvailable(int port) throws Exception {
+ ServerSocket socket = null;
+ try {
+ 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());
+ }
+
+ class SshConfigurator implements ManagedService {
+ @SuppressWarnings("rawtypes")
+ private Dictionary properties;
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public synchronized void updated(Dictionary props) throws ConfigurationException {
+ if (props != null) {
+ this.properties = props;
+ properties.put(Constants.SERVICE_PID, SSH_PID);
+ } else {
+ return;
+ }
+
+ defaultPort = Integer.parseInt(((String)properties.get(PORT)));
+ defaultHost = (String)properties.get(HOST);
+ if (properties.get(ENABLED) == null) {
+ isEnabled = false;
+ } else {
+ isEnabled = Boolean.parseBoolean((String)properties.get(ENABLED));
+ }
+ synchronized (lock) {
+ configuratorRegistration.setProperties(properties);
+ }
+ if (sshServ == null && isEnabled == true) {
+ try {
+ ssh(new String[]{"start"});
+ } catch (Exception e) {
+ System.out.println("Cannot start ssh: " + e.getMessage());
+ e.printStackTrace();
+ }
+ }
+ }
+
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/ssh/SshInputHandler.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/ssh/SshInputHandler.java
new file mode 100755
index 000000000..d1b2eb766
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/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/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/ssh/SshInputScanner.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/ssh/SshInputScanner.java
new file mode 100755
index 000000000..f0c88a934
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/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/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/ssh/SshServ.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/ssh/SshServ.java
new file mode 100755
index 000000000..a8cfed62e
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/ssh/SshServ.java
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * 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.util.List;
+
+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(List<CommandProcessor> processors, BundleContext context, String host, int port) {
+ this.host = host;
+ this.port = port;
+ shellFactory = new SshShellFactory(processors, 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();
+ }
+ }
+
+ public synchronized void addCommandProcessor(CommandProcessor processor) {
+ shellFactory.addCommandProcessor(processor);
+ }
+
+ public synchronized void removeCommandProcessor(CommandProcessor processor) {
+ shellFactory.removeCommandProcessor(processor);
+ }
+
+ private PasswordAuthenticator createJaasPasswordAuthenticator() {
+ JaasPasswordAuthenticator jaasPasswordAuthenticator = new JaasPasswordAuthenticator();
+ jaasPasswordAuthenticator.setDomain(EQUINOX_CONSOLE_DOMAIN);
+ return jaasPasswordAuthenticator;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/ssh/SshSession.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/ssh/SshSession.java
new file mode 100755
index 000000000..fe78733c5
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/ssh/SshSession.java
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Lazar Kirchev, SAP AG - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.console.ssh;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.Map;
+
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.CommandSession;
+import org.eclipse.equinox.console.common.ConsoleInputStream;
+import org.eclipse.equinox.console.common.ConsoleOutputStream;
+import org.eclipse.equinox.console.common.KEYS;
+import org.eclipse.equinox.console.common.terminal.TerminalTypeMappings;
+import org.eclipse.equinox.console.storage.SecureUserStore;
+import org.eclipse.equinox.console.supportability.ConsoleInputHandler;
+import org.eclipse.equinox.console.supportability.ConsoleInputScanner;
+import org.osgi.framework.BundleContext;
+
+/**
+ * This class manages a ssh connection. It is responsible for wrapping the original io streams
+ * from the socket, and starting a CommandSession to execute commands from the ssh.
+ *
+ */
+public class SshSession extends Thread implements Closeable {
+ private CommandProcessor processor;
+ private BundleContext context;
+ private SshShell sshShell;
+ private InputStream in;
+ private OutputStream out;
+ private TerminalTypeMappings currentMappings;
+ private Map<String, KEYS> currentEscapesToKey;
+
+ private static final String PROMPT = "prompt";
+ private static final String OSGI_PROMPT = "osgi> ";
+ private static final String SCOPE = "SCOPE";
+ private static final String EQUINOX_SCOPE = "equinox:*";
+ private static final String INPUT_SCANNER = "INPUT_SCANNER";
+ private static final String SSH_INPUT_SCANNER = "SSH_INPUT_SCANNER";
+ private static final String USER_STORAGE_PROPERTY_NAME = "osgi.console.ssh.useDefaultSecureStorage";
+ private static final String DEFAULT_USER = "equinox";
+ private static final String CLOSEABLE = "CLOSEABLE";
+ private static final int ADD_USER_COUNTER_LIMIT = 2;
+
+ public SshSession(CommandProcessor processor, BundleContext context, SshShell sshShell, InputStream in, OutputStream out, TerminalTypeMappings currentMappings, Map<String, KEYS> currentExcapesToKey) {
+ this.processor = processor;
+ this.context = context;
+ this.sshShell = sshShell;
+ this.in = in;
+ this.out = out;
+ this.currentMappings = currentMappings;
+ this.currentEscapesToKey = currentExcapesToKey;
+ }
+
+ public void run() {
+ ConsoleInputStream input = new ConsoleInputStream();
+ ConsoleOutputStream outp = new ConsoleOutputStream(out);
+ SshInputHandler inputHandler = new SshInputHandler(in, input, outp);
+ inputHandler.getScanner().setBackspace(currentMappings.getBackspace());
+ inputHandler.getScanner().setDel(currentMappings.getDel());
+ inputHandler.getScanner().setCurrentEscapesToKey(currentEscapesToKey);
+ inputHandler.getScanner().setEscapes(currentMappings.getEscapes());
+ inputHandler.start();
+
+ ConsoleInputStream inp = new ConsoleInputStream();
+ ConsoleInputHandler consoleInputHandler = new ConsoleInputHandler(input, inp, outp);
+ consoleInputHandler.getScanner().setBackspace(currentMappings.getBackspace());
+ consoleInputHandler.getScanner().setDel(currentMappings.getDel());
+ consoleInputHandler.getScanner().setCurrentEscapesToKey(currentEscapesToKey);
+ consoleInputHandler.getScanner().setEscapes(currentMappings.getEscapes());
+ ((ConsoleInputScanner)consoleInputHandler.getScanner()).setContext(context);
+ consoleInputHandler.start();
+
+ final CommandSession session;
+ final PrintStream output = new PrintStream(outp);
+
+ session = processor.createSession(inp, output, output);
+ session.put(SCOPE, EQUINOX_SCOPE);
+ session.put(PROMPT, OSGI_PROMPT);
+ session.put(INPUT_SCANNER, consoleInputHandler.getScanner());
+ session.put(SSH_INPUT_SCANNER, inputHandler.getScanner());
+ // Store this closeable object in the session, so that the disconnect command can close it
+ session.put(CLOSEABLE, this);
+ ((ConsoleInputScanner)consoleInputHandler.getScanner()).setSession(session);
+
+ try {
+ if ("true".equals(context.getProperty(USER_STORAGE_PROPERTY_NAME))) {
+ String[] names = SecureUserStore.getUserNames();
+ for (String name : names) {
+ // if the default user is the only user, request creation of a new user and delete the default
+ if (DEFAULT_USER.equals(name)) {
+ if (names.length == 1) {
+ session.getConsole().println("Currently the default user is the only one; since it will be deleted after first login, create a new user:");
+ boolean isUserAdded =false;
+ int count = 0;
+ while (!isUserAdded && count < ADD_USER_COUNTER_LIMIT ){
+ isUserAdded = ((Boolean) session.execute("addUser")).booleanValue();
+ count++;
+ }
+ if (!isUserAdded) {
+ break;
+ }
+ }
+ if (SecureUserStore.existsUser(name)) {
+ SecureUserStore.deleteUser(name);
+ }
+ break;
+ }
+ }
+ }
+ session.execute("gosh --login --noshutdown");
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ session.close();
+ }
+
+ }
+
+ public void close() throws IOException {
+ this.interrupt();
+ sshShell.removeSession(this);
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/ssh/SshShell.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/ssh/SshShell.java
new file mode 100755
index 000000000..f1f4839e3
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/ssh/SshShell.java
@@ -0,0 +1,154 @@
+/*******************************************************************************
+ * 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.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.sshd.server.Command;
+import org.apache.sshd.server.Environment;
+import org.apache.sshd.server.ExitCallback;
+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.osgi.framework.BundleContext;
+
+/**
+ * This class manages a ssh connection. It is responsible for starting a sessions to execute commands
+ * from the ssh. If there are multiple CommandProcessors, a session is started for each of them.
+ *
+ */
+public class SshShell implements Command {
+
+ private List<CommandProcessor> processors;
+ private BundleContext context;
+ private InputStream in;
+ private OutputStream out;
+ private ExitCallback callback;
+ private Map<CommandProcessor, SshSession> commandProcessorToConsoleThreadMap = new HashMap<CommandProcessor, SshSession>();
+
+ private final Map<String, TerminalTypeMappings> supportedEscapeSequences;
+ private static final String DEFAULT_TTYPE = File.separatorChar == '/' ? "XTERM" : "ANSI";
+ private TerminalTypeMappings currentMappings;
+ private Map<String, KEYS> currentEscapesToKey;
+ private static final String TERMINAL_PROPERTY = "TERM";
+
+ public SshShell(List<CommandProcessor> processors, BundleContext context) {
+ this.processors = processors;
+ 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 synchronized 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();
+ }
+
+ for (CommandProcessor processor : processors) {
+ createNewSession(processor);
+ }
+ }
+
+ public synchronized void addCommandProcessor(CommandProcessor processor) {
+ createNewSession(processor);
+ }
+
+ public synchronized void removeCommandProcessor(CommandProcessor processor) {
+ Thread consoleSession = commandProcessorToConsoleThreadMap.get(processor);
+ if (consoleSession != null) {
+ consoleSession.interrupt();
+ }
+ }
+
+ private void createNewSession(CommandProcessor processor) {
+ SshSession consoleSession = startNewConsoleSession(processor);
+ commandProcessorToConsoleThreadMap.put(processor, consoleSession);
+ }
+
+ public void destroy() {
+ return;
+ }
+
+ public void onExit() {
+ if (commandProcessorToConsoleThreadMap.values() != null) {
+ for (Thread consoleSession : commandProcessorToConsoleThreadMap.values()) {
+ consoleSession.interrupt();
+ }
+ }
+ callback.onExit(0);
+ }
+
+ public void removeSession(SshSession session) {
+ CommandProcessor processorToRemove = null;
+ for (CommandProcessor processor : commandProcessorToConsoleThreadMap.keySet()) {
+ if (session.equals(commandProcessorToConsoleThreadMap.get(processor))) {
+ processorToRemove = processor;
+ break;
+ }
+ }
+
+ if (processorToRemove != null) {
+ commandProcessorToConsoleThreadMap.remove(processorToRemove);
+ }
+
+ if (commandProcessorToConsoleThreadMap.size() == 0) {
+ onExit();
+ }
+ }
+
+ private SshSession startNewConsoleSession(CommandProcessor processor) {
+ SshSession consoleSession = new SshSession(processor, context, this, in, out, currentMappings, currentEscapesToKey);
+ consoleSession.start();
+ return consoleSession;
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/ssh/SshShellFactory.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/ssh/SshShellFactory.java
new file mode 100755
index 000000000..82b7964d9
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/ssh/SshShellFactory.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * 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.List;
+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 List<CommandProcessor> processors;
+ private BundleContext context;
+ private Set<SshShell> shells = new HashSet<SshShell>();
+
+ public SshShellFactory(List<CommandProcessor> processors, BundleContext context) {
+ this.processors = processors;
+ this.context = context;
+ }
+
+ public synchronized Command create() {
+ SshShell shell = new SshShell(processors, context);
+ shells.add(shell);
+ return shell;
+ }
+
+ public synchronized void addCommandProcessor (CommandProcessor processor) {
+ processors.add(processor);
+ for (SshShell shell : shells) {
+ shell.addCommandProcessor(processor);
+ }
+ }
+
+ public synchronized void removeCommandProcessor (CommandProcessor processor) {
+ processors.remove(processor);
+ for (SshShell shell : shells) {
+ shell.removeCommandProcessor(processor);
+ }
+ }
+
+ public void exit() {
+ for(SshShell shell : shells) {
+ shell.onExit();
+ }
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/ssh/UserAdminCommand.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/ssh/UserAdminCommand.java
new file mode 100755
index 000000000..4a57da6be
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/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/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/storage/DigestUtil.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/storage/DigestUtil.java
new file mode 100755
index 000000000..fec76ba7d
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/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/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/storage/SecureUserStore.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/storage/SecureUserStore.java
new file mode 100755
index 000000000..dc6bde77f
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/storage/SecureUserStore.java
@@ -0,0 +1,614 @@
+/*******************************************************************************
+ * 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 (Object key : users.keySet()) {
+ if (!(key instanceof String)) {
+ continue;
+ }
+ String[] parts = ((String) 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!");
+ }
+
+// Set<Object> keys = users.keySet();
+// for (Object key : keys) {
+// if ((key instanceof String) && ((String) key).contains(DELIMITER + username + DELIMITER)) {
+// users.remove(key);
+// }
+// }
+ String rolesProperty = constructPropertyName(username, ROLES_KEY);
+ String passwordProperty = constructPropertyName(username, PASSWORD_KEY);
+
+ users.remove(rolesProperty);
+ users.remove(passwordProperty);
+
+ 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 (Object key : users.keySet()) {
+ if (key instanceof String && ((String) 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 (Object key : users.keySet()) {
+ if ((key instanceof String) && ((String) 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 (Object user : users.keySet()) {
+ if (user instanceof String && ((String) user).contains(DELIMITER + username + DELIMITER)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/supportability/ConsoleInputHandler.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/supportability/ConsoleInputHandler.java
new file mode 100755
index 000000000..45add085e
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/supportability/ConsoleInputHandler.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2010, 2011 SAP AG 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:
+ * Lazar Kirchev, SAP AG - initial API and implementation
+ * IBM Corporation - ongoing development
+ *******************************************************************************/
+package org.eclipse.equinox.console.supportability;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.eclipse.equinox.console.common.ConsoleInputStream;
+import org.eclipse.equinox.console.common.InputHandler;
+
+/**
+ * This class customizes the generic handler with a concrete content processor,
+ * 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/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/supportability/ConsoleInputScanner.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/supportability/ConsoleInputScanner.java
new file mode 100755
index 000000000..e9e47fb17
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/supportability/ConsoleInputScanner.java
@@ -0,0 +1,502 @@
+/*******************************************************************************
+ * 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Lazar Kirchev, SAP AG - initial API and implementation
+ *******************************************************************************/
+
+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;
+
+/**
+ * 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, 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) {
+ 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) {
+ isCR = false;
+ if (b == LF) {
+ return;
+ }
+ }
+
+ if (b != TAB) {
+ if (isCompletionMode == true) {
+ isCompletionMode = false;
+ candidates = null;
+ originalCursorPos = 0;
+ }
+ }
+
+ if (isEsc) {
+ scanEsc(b);
+ } else {
+ if (b == getBackspace()) {
+ backSpace();
+ } else if(b == TAB) {
+ if (isCompletionMode == false) {
+ isCompletionMode = true;
+ processTab();
+ } else {
+ processNextTab();
+ }
+ } else if (b == CR) {
+ isCR = true;
+ processData();
+ } else if (b == LF) {
+ processData();
+ } else if (b == ESC) {
+ startEsc();
+ } else if (b == getDel()) {
+ delete();
+ } else {
+ if (b >= SPACE && b < MAX_CHAR) {
+ newChar(b);
+ }
+ }
+ }
+ }
+
+ private void delete() throws IOException {
+ clearLine();
+ buffer.delete();
+ echoBuff();
+ flush();
+ }
+
+ private void backSpace() throws IOException {
+ clearLine();
+ buffer.backSpace();
+ echoBuff();
+ flush();
+ }
+
+ protected void clearLine() throws IOException {
+ int size = buffer.getSize();
+ int pos = buffer.getPos();
+ for (int i = size - pos; i < size; i++) {
+ echo(BS);
+ }
+ for (int i = 0; i < size; i++) {
+ echo(SPACE);
+ }
+ for (int i = 0; i < size; i++) {
+ echo(BS);
+ }
+ }
+
+ protected void echoBuff() throws IOException {
+ byte[] data = buffer.copyCurrentData();
+ for (byte b : data) {
+ echo(b);
+ }
+ int pos = buffer.getPos();
+ for (int i = data.length; i > pos; i--) {
+ echo(BS);
+ }
+ }
+
+ protected void newChar(int b) throws IOException {
+ if (buffer.getPos() < buffer.getSize()) {
+ if (replace) {
+ buffer.replace(b);
+ } else {
+ buffer.insert(b);
+ }
+ clearLine();
+ echoBuff();
+ flush();
+ } else {
+ if (replace) {
+ buffer.replace(b);
+ } else {
+ buffer.insert(b);
+ }
+ }
+ }
+
+ 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(LF);
+ echo(CR);
+ echo(LF);
+ flush();
+ byte[] curr = buffer.getCurrentData();
+ if (isHistoryEnabled == true) {
+ history.add(curr);
+ }
+ toShell.add(curr);
+ }
+
+ public void resetHistory() {
+ history.reset();
+ }
+
+ protected void scanEsc(final int b) throws IOException {
+ esc += (char) b;
+ KEYS key = checkEscape(esc);
+ if (key == KEYS.UNFINISHED) {
+ return;
+ }
+ if (key == KEYS.UNKNOWN) {
+ isEsc = false;
+ scan(b);
+ return;
+ }
+ isEsc = false;
+ switch (key) {
+ case UP:
+ processUpArrow();
+ break;
+ case DOWN:
+ processDownArrow();
+ break;
+ case RIGHT:
+ processRightArrow();
+ break;
+ case LEFT:
+ processLeftArrow();
+ break;
+ case HOME:
+ processHome();
+ break;
+ case END:
+ processEnd();
+ break;
+ case PGUP:
+ processPgUp();
+ break;
+ case PGDN:
+ processPgDn();
+ break;
+ case INS:
+ processIns();
+ break;
+ case DEL:
+ delete();
+ break;
+ default: //CENTER
+ break;
+ }
+ }
+
+ private static final byte[] INVERSE_ON = {ESC, '[', '7', 'm'};
+ private static final byte[] INVERSE_OFF = {ESC, '[', '2', '7', 'm'};
+
+ private void echo(byte[] data) throws IOException {
+ for (byte b : data) {
+ echo(b);
+ }
+ }
+
+ private void processIns() throws IOException {
+ replace = !replace;
+ int b = buffer.getCurrentChar();
+ echo(INVERSE_ON);
+ echo(replace ? 'R' : 'I');
+ flush();
+ try {
+ Thread.sleep(300);
+ } catch (InterruptedException e) {
+ //do not care $JL-EXC$
+ }
+ echo(INVERSE_OFF);
+ echo(BS);
+ echo(b == -1 ? SPACE : b);
+ echo(BS);
+ flush();
+ }
+
+ private void processPgDn() throws IOException {
+ byte[] last = history.last();
+ if (last != null) {
+ clearLine();
+ buffer.set(last);
+ echoBuff();
+ flush();
+ }
+ }
+
+ private void processPgUp() throws IOException {
+ byte[] first = history.first();
+ if (first != null) {
+ clearLine();
+ buffer.set(first);
+ echoBuff();
+ flush();
+ }
+ }
+
+ private void processHome() throws IOException {
+ int pos = buffer.resetPos();
+ if (pos > 0) {
+ for (int i = 0; i < pos; i++) {
+ echo(BS);
+ }
+ flush();
+ }
+ }
+
+ private void processEnd() throws IOException {
+ int b;
+ while ((b = buffer.goRight()) != -1) {
+ echo(b);
+ }
+ flush();
+ }
+
+ private void processLeftArrow() throws IOException {
+ if (buffer.goLeft()) {
+ echo(BS);
+ flush();
+ }
+ }
+
+ private void processRightArrow() throws IOException {
+ int b = buffer.goRight();
+ if (b != -1) {
+ echo(b);
+ flush();
+ }
+ }
+
+ private void processDownArrow() throws IOException {
+ byte[] next = history.next();
+ if (next != null) {
+ clearLine();
+ buffer.set(next);
+ echoBuff();
+ flush();
+ }
+ }
+
+ private void processUpArrow() throws IOException {
+ clearLine();
+ byte[] prev = history.prev();
+ buffer.set(prev);
+ 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/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/supportability/HistoryHolder.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/supportability/HistoryHolder.java
new file mode 100755
index 000000000..52bdd429a
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/supportability/HistoryHolder.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Lazar Kirchev, SAP AG - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.equinox.console.supportability;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+
+/**
+ * A helper class, which implements history.
+ */
+public class HistoryHolder {
+
+ private static final int MAX = 100;
+ private final byte[][] history;
+ private int size;
+ private int pos;
+
+ public HistoryHolder() {
+ history = new byte[MAX][];
+ }
+
+ public synchronized void reset() {
+ size = 0;
+ pos = 0;
+ for (int i = 0; i < MAX; i++) {
+ history[i] = null;
+ }
+ }
+
+ public synchronized void add(byte[] data) {
+ try {
+ data = new String(data, "US-ASCII").trim().getBytes("US-ASCII");
+ } catch (UnsupportedEncodingException e) {
+
+ }
+ if (data.length == 0) {
+ pos = size;
+ return;
+ }
+ for (int i = 0; i < size; i++) {
+ if (Arrays.equals(history[i], data)) {
+ System.arraycopy(history, i + 1, history, i, size - i - 1);
+ history[size - 1] = data;
+ pos = size;
+ return;
+ }
+ }
+ if (size >= MAX) {
+ System.arraycopy(history, 1, history, 0, size - 1);
+ size--;
+ }
+ history[size++] = data;
+ pos = size;
+ }
+
+ public synchronized byte[] next() {
+ if (pos >= size - 1) {
+ return null;
+ }
+ return history[++pos];
+ }
+
+ public synchronized byte[] last() {
+ if (size > 0) {
+ pos = size - 1;
+ return history[pos];
+ } else {
+ return null;
+ }
+ }
+
+ public synchronized byte[] first() {
+ if (size > 0) {
+ pos = 0;
+ return history[pos];
+ } else {
+ return null;
+ }
+ }
+
+ public synchronized byte[] prev() {
+ if (size == 0) {
+ return null;
+ }
+ if (pos == 0) {
+ return history[pos];
+ } else {
+ return history[--pos];
+ }
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/Callback.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/Callback.java
new file mode 100755
index 000000000..2874b3c74
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/Callback.java
@@ -0,0 +1,17 @@
+/*******************************************************************************
+ * 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Lazar Kirchev, SAP AG - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.equinox.console.telnet;
+
+public interface Callback {
+
+ public void finished();
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/NegotiationFinishedCallback.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/NegotiationFinishedCallback.java
new file mode 100755
index 000000000..c46811816
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/NegotiationFinishedCallback.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Lazar Kirchev, SAP AG - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.equinox.console.telnet;
+/**
+ * A callback through which the TelnetInputScanner notifies the TelnetConnectionManger
+ * that the terminal type negotiation with the client has finished. This is importednt, because
+ * the TelnetConnectionManager should start the CommandSession after the negotiation is finished.
+ * 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) {
+ this.telnetConnection = telnetConnection;
+ }
+
+ public void finished() {
+ telnetConnection.telnetNegotiationFinished();
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/TelnetCommand.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/TelnetCommand.java
new file mode 100755
index 000000000..5673a3035
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/TelnetCommand.java
@@ -0,0 +1,244 @@
+/*******************************************************************************
+ * 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Lazar Kirchev, SAP AG - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.equinox.console.telnet;
+
+import java.net.BindException;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.Descriptor;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+
+/**
+ * This class implements a command for starting/stopping a simple telnet server.
+ *
+ */
+public class TelnetCommand {
+
+ private String defaultHost = null;
+ private int defaultPort;
+ private List<CommandProcessor> processors = new ArrayList<CommandProcessor>();
+ private final BundleContext context;
+ private String host = null;
+ private int port;
+ private TelnetServer telnetServer = null;
+ private ServiceRegistration<?> configuratorRegistration;
+ private boolean isEnabled = false;
+
+ private static final String HOST = "host";
+ private static final String PORT = "port";
+ private static final String USE_CONFIG_ADMIN_PROP = "osgi.console.useConfigAdmin";
+ private static final String TELNET_PID = "osgi.console.telnet";
+ private static final String CONSOLE_PROP = "osgi.console";
+ private static final String ENABLED = "enabled";
+ private final Object lock = new Object();
+
+ public TelnetCommand(CommandProcessor processor, BundleContext context)
+ {
+ processors.add(processor);
+ this.context = context;
+ if ("true".equals(context.getProperty(USE_CONFIG_ADMIN_PROP))) {
+ Dictionary<String, String> telnetProperties = new Hashtable<String, String>();
+ telnetProperties.put(Constants.SERVICE_PID, TELNET_PID);
+ try {
+ synchronized (lock) {
+ configuratorRegistration = context.registerService(ManagedService.class.getName(), new TelnetConfigurator(), telnetProperties);
+ }
+ } catch (NoClassDefFoundError e) {
+ System.out.println("Configuration Admin not available!");
+ return;
+ }
+ } else {
+ parseHostAndPort();
+ }
+ }
+
+ private void parseHostAndPort() {
+ String telnetPort = null;
+ String consolePropValue = context.getProperty(CONSOLE_PROP);
+ if(consolePropValue != null) {
+ int index = consolePropValue.lastIndexOf(":");
+ if (index > -1) {
+ defaultHost = consolePropValue.substring(0, index);
+ }
+ telnetPort = consolePropValue.substring(index + 1);
+ isEnabled = true;
+ }
+ if (telnetPort != null && !"".equals(telnetPort)) {
+ try {
+ defaultPort = Integer.parseInt(telnetPort);
+ } 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[] {"telnet"});
+ if ((port > 0 || defaultPort > 0) && isEnabled == true) {
+ 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")
+ public synchronized void telnet(String[] arguments) throws Exception
+ {
+ String command = null;
+ int newPort = 0;
+ String newHost = null;
+
+ 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 telnet command/option " + arguments[i]);
+ }
+ }
+
+ if (command == null) {
+ throw new Exception("No telnet command specified");
+ }
+
+ if (newPort != 0) {
+ port = newPort;
+ } else if (port == 0) {
+ port = defaultPort;
+ }
+
+ if (port == 0) {
+ throw new Exception("No telnet port specified");
+ }
+
+ if (newHost != null) {
+ host = newHost;
+ } else {
+ host = defaultHost;
+ }
+
+ if ("start".equals(command)) {
+ if (telnetServer != null) {
+ throw new IllegalStateException("telnet is already running on port " + port);
+ }
+
+ try {
+ telnetServer = new TelnetServer(context, processors, host, port);
+ } catch (BindException e) {
+ throw new Exception("Port " + port + " already in use");
+ }
+
+ telnetServer.setName("equinox telnet");
+ telnetServer.start();
+ } else if ("stop".equals(command)) {
+ if (telnetServer == null) {
+ throw new IllegalStateException("telnet is not running.");
+ }
+
+ telnetServer.stopTelnetServer();
+ telnetServer = null;
+ }
+ }
+
+ public synchronized void addCommandProcessor(CommandProcessor processor) {
+ processors.add(processor);
+ telnetServer.addCommandProcessor(processor);
+ }
+
+ public synchronized void removeCommandProcessor(CommandProcessor processor) {
+ processors.remove(processor);
+ telnetServer.removeCommandProcessor(processor);
+ }
+
+ private void printHelp() {
+ StringBuffer help = new StringBuffer();
+ help.append("telnet - start simple telnet server");
+ help.append("\n");
+ help.append("Usage: telnet 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());
+ }
+
+ class TelnetConfigurator implements ManagedService {
+ @SuppressWarnings("rawtypes")
+ private Dictionary properties;
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public synchronized void updated(Dictionary props) throws ConfigurationException {
+ if (props != null) {
+ this.properties = props;
+ properties.put(Constants.SERVICE_PID, TELNET_PID);
+ } else {
+ return;
+ }
+
+ defaultPort = Integer.parseInt(((String)properties.get(PORT)));
+ defaultHost = (String)properties.get(HOST);
+ if (properties.get(ENABLED) == null) {
+ isEnabled = false;
+ } else {
+ isEnabled = Boolean.parseBoolean((String)properties.get(ENABLED));
+ }
+ synchronized (lock) {
+ configuratorRegistration.setProperties(properties);
+ }
+ if (telnetServer == null && isEnabled == true) {
+ try {
+ telnet(new String[]{"start"});
+ } catch (Exception e) {
+ System.out.println("Cannot start telnet: " + e.getMessage());
+ e.printStackTrace();
+ }
+ }
+ }
+
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/TelnetConnection.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/TelnetConnection.java
new file mode 100755
index 000000000..56802cf6e
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/TelnetConnection.java
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * Copyright (c) 2010, 2011 SAP AG 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:
+ * Lazar Kirchev, SAP AG - initial API and implementation
+ * IBM Corporation - ongoing development
+ *******************************************************************************/
+package org.eclipse.equinox.console.telnet;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.net.Socket;
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.CommandSession;
+import java.io.Closeable;
+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
+ * from the socket, and starting a CommandSession to execute commands from the telnet.
+ *
+ */
+public class TelnetConnection extends Thread implements Closeable {
+
+ private Socket socket;
+ private CommandProcessor processor;
+ private BundleContext context;
+ protected boolean isTelnetNegotiationFinished = false;
+ private Callback callback;
+ 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 SCOPE = "SCOPE";
+ private static final String EQUINOX_SCOPE = "equinox:*";
+ private static final String CLOSEABLE = "CLOSEABLE";
+
+ public TelnetConnection (Socket socket, CommandProcessor processor, BundleContext context) {
+ this.socket = socket;
+ this.processor = processor;
+ this.context = context;
+ callback = new NegotiationFinishedCallback(this);
+ }
+
+ public void run() {
+ try {
+ ConsoleInputStream in = new ConsoleInputStream();
+ TelnetOutputStream out = new TelnetOutputStream(socket.getOutputStream());
+ out.autoSend();
+ TelnetInputHandler telnetInputHandler = new TelnetInputHandler(socket.getInputStream(), in, out, callback);
+ telnetInputHandler.start();
+
+ long start = System.currentTimeMillis();
+
+ synchronized (this) {
+ while (isTelnetNegotiationFinished == false && System.currentTimeMillis() - start < NEGOTIATION_TIMEOUT) {
+ try {
+ wait(TIMEOUT);
+ } catch (InterruptedException e) {
+ // do nothing
+ }
+ }
+ }
+ final CommandSession session;
+ PrintStream output = new PrintStream(out);
+
+ ConsoleInputStream inp = new ConsoleInputStream();
+
+ ConsoleInputHandler consoleInputHandler = new ConsoleInputHandler(in, inp, out);
+ consoleInputHandler.getScanner().setBackspace(telnetInputHandler.getScanner().getBackspace());
+ 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(SCOPE, EQUINOX_SCOPE);
+ session.put(PROMPT, OSGI_PROMPT);
+ // Store this closeable object in the session, so that the disconnect command can close it
+ session.put(CLOSEABLE, this);
+ ((ConsoleInputScanner)consoleInputHandler.getScanner()).setSession(session);
+
+ try {
+ session.execute("gosh --login --noshutdown");
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ session.close();
+ try {
+ socket.close();
+ }
+ catch (IOException e) {
+ // do nothing
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void close() {
+ try {
+ this.interrupt();
+ socket.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public synchronized void telnetNegotiationFinished() {
+ isTelnetNegotiationFinished = true;
+ notify();
+ }
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/TelnetInputHandler.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/TelnetInputHandler.java
new file mode 100755
index 000000000..0be072d53
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/TelnetInputHandler.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2010, 2011 SAP AG 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:
+ * Lazar Kirchev, SAP AG - initial API and implementation
+ * IBM Corporation - ongoing development
+ *******************************************************************************/
+package org.eclipse.equinox.console.telnet;
+
+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 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/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/TelnetInputScanner.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/TelnetInputScanner.java
new file mode 100755
index 000000000..57f9af307
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/TelnetInputScanner.java
@@ -0,0 +1,281 @@
+/*******************************************************************************
+ * Copyright (c) 2010, 2011 SAP AG 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:
+ * Lazar Kirchev, SAP AG - initial API and implementation
+ * IBM Corporation - ongoing development
+ *******************************************************************************/
+package org.eclipse.equinox.console.telnet;
+
+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 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
+ * ANSI, VT100, VT220, VT320, XTERM and SCO. The support is limited to the following
+ * keys - BACKSPACE, DEL, INSERT, HOME, END, PAGEUP, PAGEDOWN, ARROWS.
+ */
+public class TelnetInputScanner extends Scanner {
+
+ private boolean isCommand = false;
+ private boolean isReadingTtype = false;
+ private boolean shouldFinish = false;
+ private boolean tTypeNegotiationStarted = false;
+ private int lastRead = -1;
+ private ArrayList<Integer> currentTerminalType = new ArrayList<Integer>();
+ private ArrayList<Integer> lastTerminalType = null;
+ private Set<String> supportedTerminalTypes = new HashSet<String>();
+ private Callback callback;
+
+ public TelnetInputScanner(ConsoleInputStream toShell, ConsoleOutputStream toTelnet, Callback callback) {
+ super(toShell, toTelnet);
+ initializeSupportedTerminalTypes();
+ TerminalTypeMappings currentMapping = supportedEscapeSequences.get(DEFAULT_TTYPE);
+ currentEscapesToKey = currentMapping.getEscapesToKey();
+ escapes = currentMapping.getEscapes();
+ setBackspace(currentMapping.getBackspace());
+ setDel(currentMapping.getDel());
+ this.callback = callback;
+ }
+
+ private void initializeSupportedTerminalTypes() {
+ supportedTerminalTypes.add("ANSI");
+ supportedTerminalTypes.add("VT100");
+ supportedTerminalTypes.add("VT220");
+ supportedTerminalTypes.add("VT320");
+ supportedTerminalTypes.add("XTERM");
+ supportedTerminalTypes.add("SCO");
+ }
+
+ public void scan(int b) throws IOException {
+ b &= 0xFF;
+
+ if (isEsc) {
+ scanEsc(b);
+ } else if (isCommand) {
+ scanCommand(b);
+ } else if (b == IAC) {
+ startCommand();
+ } 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});
+ }
+
+ }
+ lastRead = b;
+ }
+
+ /* Telnet command codes are described in RFC 854, TELNET PROTOCOL SPECIFICATION
+ * available at http://www.ietf.org/rfc/rfc854.txt
+ *
+ * Telnet terminal type negotiation option is described in RFC 1091, Telnet Terminal-Type Option
+ * available at http://www.ietf.org/rfc/rfc1091.txt
+ */
+ private static final int SE = 240;
+ private static final int EC = 247;
+ private static final int EL = 248;
+ private static final int SB = 250;
+ private static final int WILL = 251;
+ private static final int WILL_NOT = 252;
+ private static final int DO = 253;
+ private static final int DO_NOT = 254;
+ private static final int TTYPE = 24;
+ private static final int SEND = 1;
+ private static final int IAC = 255;
+ private static final int IS = 0;
+
+ private boolean isNegotiation;
+ private boolean isWill;
+
+ private byte[] tTypeRequest = {(byte)IAC, (byte)SB, (byte)TTYPE, (byte)SEND, (byte)IAC, (byte)SE};
+
+ private void scanCommand(final int b) throws IOException {
+ if (isNegotiation) {
+ scanNegotiation(b);
+ } else if (isWill) {
+ isWill = false;
+ isCommand = false;
+ if(b == TTYPE && tTypeNegotiationStarted == false) {
+ sendRequest();
+ }
+ } else {
+ switch (b) {
+ case WILL:
+ isWill = true;
+ break;
+ case WILL_NOT:
+ break;
+ case DO:
+ break;
+ case DO_NOT:
+ break;
+ case SB:
+ isNegotiation = true;
+ break;
+ case EC:
+ eraseChar();
+ isCommand = false;
+ break;
+ case EL:
+ default:
+ isCommand = false;
+ break;
+ }
+ }
+ }
+
+ private void scanNegotiation(final int b) {
+ if (lastRead == SB && b == TTYPE) {
+ isReadingTtype = true;
+ } else if (b == IS) {
+
+ } else if (b == IAC) {
+
+ } else if (b == SE) {
+ isNegotiation = false;
+ isCommand = false;
+ if (isReadingTtype == true) {
+ isReadingTtype = false;
+ if (shouldFinish == true) {
+ setCurrentTerminalType();
+ shouldFinish = false;
+ return;
+ }
+ boolean isMatch = isTerminalTypeSupported();
+ boolean isLast = isLast();
+ if (isMatch == true) {
+ setCurrentTerminalType();
+ return;
+ }
+ lastTerminalType = currentTerminalType;
+ currentTerminalType = new ArrayList<Integer>();
+ if (isLast == true && isMatch == false) {
+ shouldFinish = true;
+ sendRequest();
+ } else if (isLast == false && isMatch == false) {
+ sendRequest();
+ }
+ }
+ } else if (isReadingTtype == true){
+ currentTerminalType.add(b);
+ }
+ }
+
+ private boolean isTerminalTypeSupported() {
+ byte[] tmp = new byte[currentTerminalType.size()];
+ int idx = 0;
+ for(Integer i : currentTerminalType) {
+ tmp[idx] = i.byteValue();
+ idx++;
+ }
+ String tType = new String(tmp);
+
+ for(String terminal : supportedTerminalTypes) {
+ if(tType.toUpperCase().contains(terminal)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private boolean isLast() {
+ if(currentTerminalType.equals(lastTerminalType)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private void setCurrentTerminalType() {
+ byte[] tmp = new byte[currentTerminalType.size()];
+ int idx = 0;
+ for(Integer i : currentTerminalType) {
+ tmp[idx] = i.byteValue();
+ idx++;
+ }
+ String tType = new String(tmp);
+ String term = null;
+ for(String terminal : supportedTerminalTypes) {
+ if(tType.toUpperCase().contains(terminal)) {
+ term = terminal;
+ }
+ }
+ TerminalTypeMappings currentMapping = supportedEscapeSequences.get(term);
+ if(currentMapping == null) {
+ currentMapping = supportedEscapeSequences.get(DEFAULT_TTYPE);
+ }
+ currentEscapesToKey = currentMapping.getEscapesToKey();
+ escapes = currentMapping.getEscapes();
+ setBackspace(currentMapping.getBackspace());
+ setDel(currentMapping.getDel());
+ if(callback != null) {
+ callback.finished();
+ }
+ }
+
+ private void sendRequest() {
+ try {
+ toTelnet.write(tTypeRequest);
+ toTelnet.flush();
+ if(tTypeNegotiationStarted == false) {
+ tTypeNegotiationStarted = true;
+ }
+ } catch (IOException e) {
+
+ e.printStackTrace();
+ }
+ }
+
+ private void startCommand() {
+ isCommand = true;
+ isNegotiation = false;
+ isWill = false;
+ }
+
+ private void eraseChar() throws IOException {
+ toShell.add(new byte[]{BS});
+ }
+
+ 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/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/TelnetOutputStream.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/TelnetOutputStream.java
new file mode 100755
index 000000000..4d46494c2
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/TelnetOutputStream.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Lazar Kirchev, SAP AG - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.equinox.console.telnet;
+
+import org.eclipse.equinox.console.common.ConsoleOutputStream;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * 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
+ (byte) 255, (byte) 253, (byte) 24}; // IAC DO TTYPE
+
+ public TelnetOutputStream(OutputStream out) {
+ super(out);
+ }
+
+ /**
+ * Sends the options which a server wants to negotiate with a telnet client.
+ */
+ public synchronized void autoSend() throws IOException {
+ write(autoMessage);
+ flush();
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/TelnetServer.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/TelnetServer.java
new file mode 100755
index 000000000..3ea618918
--- /dev/null
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/telnet/TelnetServer.java
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * Copyright (c) 2010, 2011 SAP AG 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:
+ * Lazar Kirchev, SAP AG - initial API and implementation
+ * IBM Corporation - ongoing development
+ *******************************************************************************/
+package org.eclipse.equinox.console.telnet;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.service.command.CommandProcessor;
+import org.osgi.framework.BundleContext;
+
+/**
+ * A telnet server, which listens for telnet connections and starts a telnet connection manager
+ * when a connection is accepted. If there are multiple CommandProcessor, a telnet connection
+ * is created for each of them.
+ *
+ */
+public class TelnetServer extends Thread {
+
+ private ServerSocket server;
+ private boolean isRunning = true;
+ private List<CommandProcessor> processors = null;
+ private BundleContext context;
+ private List<Socket> sockets = new ArrayList<Socket>();
+ private Map<CommandProcessor, List<TelnetConnection>> processorToConnectionsMapping = new HashMap<CommandProcessor, List<TelnetConnection>>();
+
+ public TelnetServer(BundleContext context, List<CommandProcessor> processors, String host, int port) throws IOException {
+ this.context = context;
+ this.processors = processors;
+ if(host != null) {
+ server = new ServerSocket(port, 0, InetAddress.getByName(host));
+ } else {
+ server = new ServerSocket(port);
+ }
+ }
+
+ public void run()
+ {
+ try
+ {
+ while (isRunning)
+ {
+ final Socket socket = server.accept();
+ sockets.add(socket);
+ for (CommandProcessor processor : processors) {
+ TelnetConnection telnetConnection = new TelnetConnection(socket, processor, context);
+ List<TelnetConnection> telnetConnections = processorToConnectionsMapping.get(processor);
+ if (telnetConnections == null) {
+ telnetConnections = new ArrayList<TelnetConnection>();
+ processorToConnectionsMapping.put(processor, telnetConnections);
+ }
+ telnetConnections.add(telnetConnection);
+ telnetConnection.start();
+ }
+ }
+ } catch (IOException e) {
+ if (isRunning == true) {
+ e.printStackTrace();
+ }
+ } finally {
+ isRunning = false;
+ try {
+ if (server != null) {
+ server.close();
+ }
+ } catch (IOException e){
+ // do nothing
+ }
+ }
+ }
+
+ public synchronized void addCommandProcessor(CommandProcessor processor) {
+ List<TelnetConnection> telnetConnections = new ArrayList<TelnetConnection>();
+ for (Socket socket : sockets) {
+ TelnetConnection telnetConnection = new TelnetConnection(socket, processor, context);
+ telnetConnections.add(telnetConnection);
+ telnetConnection.start();
+ }
+ processorToConnectionsMapping.put(processor, telnetConnections);
+ }
+
+ public synchronized void removeCommandProcessor(CommandProcessor processor) {
+ List<TelnetConnection> telnetConnections = processorToConnectionsMapping.remove(processor);
+ if (telnetConnections != null) {
+ for (TelnetConnection telnetConnection : telnetConnections) {
+ telnetConnection.close();
+ }
+ }
+ }
+
+ public synchronized void stopTelnetServer() {
+ isRunning = false;
+ try {
+ if (server != null) {
+ server.close();
+ }
+ } catch (IOException e){
+ // do nothing
+ }
+
+ for(List<TelnetConnection> telnetConnections : processorToConnectionsMapping.values()) {
+ for (TelnetConnection telnetConnection : telnetConnections) {
+ telnetConnection.close();
+ }
+ }
+
+ this.interrupt();
+ }
+}
diff --git a/bundles/org.eclipse.equinox.slf4j.stub/.classpath b/bundles/org.eclipse.equinox.slf4j.stub/.classpath
new file mode 100755
index 000000000..c83506765
--- /dev/null
+++ b/bundles/org.eclipse.equinox.slf4j.stub/.classpath
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.equinox.slf4j.stub/.project b/bundles/org.eclipse.equinox.slf4j.stub/.project
new file mode 100755
index 000000000..cac65e519
--- /dev/null
+++ b/bundles/org.eclipse.equinox.slf4j.stub/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.equinox.slf4j.stub</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/bundles/org.eclipse.equinox.slf4j.stub/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.equinox.slf4j.stub/.settings/org.eclipse.jdt.core.prefs
new file mode 100755
index 000000000..4f0ba5ed7
--- /dev/null
+++ b/bundles/org.eclipse.equinox.slf4j.stub/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+#Tue Jun 07 08:08:08 CDT 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/bundles/org.eclipse.equinox.slf4j.stub/.settings/org.eclipse.pde.core.prefs b/bundles/org.eclipse.equinox.slf4j.stub/.settings/org.eclipse.pde.core.prefs
new file mode 100755
index 000000000..9b902ed4e
--- /dev/null
+++ b/bundles/org.eclipse.equinox.slf4j.stub/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,4 @@
+#Tue Jun 07 08:08:08 CDT 2011
+eclipse.preferences.version=1
+pluginProject.extensions=false
+resolve.requirebundle=false
diff --git a/bundles/org.eclipse.equinox.slf4j.stub/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.slf4j.stub/META-INF/MANIFEST.MF
new file mode 100755
index 000000000..7ab97499a
--- /dev/null
+++ b/bundles/org.eclipse.equinox.slf4j.stub/META-INF/MANIFEST.MF
@@ -0,0 +1,7 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: slf4j Stub
+Bundle-SymbolicName: org.eclipse.equinox.slf4j.stub
+Bundle-Version: 1.0.0.qualifier
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Provide-Capability: generic; generic="org.slf4j.impl.StaticLoggerBinder"; version:Version="1.6.1"
diff --git a/bundles/org.eclipse.equinox.slf4j.stub/META-INF/p2.inf b/bundles/org.eclipse.equinox.slf4j.stub/META-INF/p2.inf
new file mode 100755
index 000000000..b0b7a6cb9
--- /dev/null
+++ b/bundles/org.eclipse.equinox.slf4j.stub/META-INF/p2.inf
@@ -0,0 +1,9 @@
+# this file is a workaround for bug 313553
+# https://bugs.eclipse.org/bugs/show_bug.cgi?id=313553
+#
+# see also
+# https://bugs.eclipse.org/bugs/show_bug.cgi?id=177851#c9
+
+provides.0.namespace = org.slf4j.api
+provides.0.name = org.slf4j.impl.StaticLoggerBinder
+provides.0.version = 1.6.1
diff --git a/bundles/org.eclipse.equinox.slf4j.stub/about.html b/bundles/org.eclipse.equinox.slf4j.stub/about.html
new file mode 100755
index 000000000..460233046
--- /dev/null
+++ b/bundles/org.eclipse.equinox.slf4j.stub/about.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+
+<p>June 2, 2006</p>
+<h3>License</h3>
+
+<p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 (&quot;EPL&quot;). A copy of the EPL is available
+at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is
+being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may
+apply to your use of any object code in the Content. Check the Redistributor's license that was
+provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org">http://www.eclipse.org</a>.</p>
+
+</body>
+</html> \ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.slf4j.stub/build.properties b/bundles/org.eclipse.equinox.slf4j.stub/build.properties
new file mode 100755
index 000000000..401b13f73
--- /dev/null
+++ b/bundles/org.eclipse.equinox.slf4j.stub/build.properties
@@ -0,0 +1,14 @@
+###############################################################################
+# Copyright (c) 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
+###############################################################################
+bin.includes = META-INF/,\
+ .,\
+ about.html
+src.includes = about.html

Back to the top