Permalink
Browse files

knapsack: a new strategy for netcat client: by default use a separate

jvm process to copy the command line from the shell session to the
input socket in the running knapsack instance.  This should work on
all platforms since Java is an inherent requirement, but it is slow
since a new jvm instance needs to be created on each call.  The
default can be overridden by specifying another command 'client' such
as netcat via the system property org.knapsack.shell.command.  This is
minimally documented in the felix.conf file.
  • Loading branch information...
1 parent a956901 commit 4f78a8483558e59248fe445bd67960d0d3ad236b @kgilmer committed Jul 25, 2011
View
@@ -3,6 +3,5 @@
<classpathentry kind="src" path=""/>
<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 combineaccessrules="false" kind="src" path="/Sprinkles"/>
<classpathentry kind="output" path=""/>
</classpath>
View
@@ -4,3 +4,4 @@ default.properties
org/apache
org/osgi
org/sprinkles
+bin/
View
@@ -16,6 +16,9 @@ org.knapsack.configAdmin.overwrite = false
# org.knapsack.scripts.disable = true
# If set to true, knapsack will accept socket connections from any host, not just 127.0.0.1
# org.knapsack.scripts.acceptAnyHost
+# Override the default netcat implementation, which is Java-based. Using a native netcat client will significantly speed up the shell performance, but may require tuning for a specific environment
+# An example: org.knapsack.shell.command = nc
+org.knapsack.shell.command = java -cp $KNAPSACK_JAR org.knapsack.shell.Netcat
# ----- Felix properties
org.osgi.framework.storage.clean = onFirstInit
@@ -18,6 +18,7 @@
import java.io.File;
import java.io.IOException;
+import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
@@ -89,7 +90,7 @@ public static void main(String[] args) {
if (!config.containsKey(Config.CONFIG_DISABLE_SCRIPTS) || !config.getBoolean(Config.CONFIG_DISABLE_SCRIPTS)) {
Random r = new Random();
port = PORT_START + r.nextInt(MAX_PORT_RANGE);
- createKnapsackScripts(baseDirectory, port);
+ createKnapsackScripts(baseDirectory, port, config);
}
// Create activators that will start
@@ -173,14 +174,13 @@ private static void createKnapsackLayout(File baseDirectory, Logger logger) thro
FSHelper.validateFile(configAdminDir, true, true, false, true);
}
- private static void createKnapsackScripts(File baseDirectory, int port) throws IOException {
+ private static void createKnapsackScripts(File baseDirectory, int port, Config config) throws IOException, URISyntaxException {
scriptDir = new File(baseDirectory, Config.SCRIPT_DIRECTORY_NAME);
FSHelper.validateFile(scriptDir, true, true, false, true);
if (FSHelper.directoryHasFiles(scriptDir))
FSHelper.deleteFilesInDir(scriptDir);
- FSHelper.copyScripts(baseDirectory, port);
-
+ FSHelper.copyScripts(baseDirectory, port, config.getProperty(Config.CONFIG_KEY_SHELL_COMMAND));
}
}
@@ -91,6 +91,8 @@
public static final String CONFIG_KEY_ACCEPT_ANY_HOST = "org.knapsack.scripts.acceptAnyHost";
+ public static final String CONFIG_KEY_SHELL_COMMAND = "org.knapsack.shell.command";
+
/**
* Base directory where knapsack instance is running.
*/
@@ -5,6 +5,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.net.URISyntaxException;
import java.util.Arrays;
import org.apache.commons.io.IOUtils;
@@ -29,9 +30,10 @@ private FSHelper() {
*
* @param baseDirectory
* @throws IOException
+ * @throws URISyntaxException
* @throws InterruptedException
*/
- public static void copyScripts(File baseDirectory, int shellPort) throws IOException {
+ public static void copyScripts(File baseDirectory, int shellPort, String command) throws IOException, URISyntaxException {
File scriptDir = new File(baseDirectory, "bin");
if (!scriptDir.exists())
@@ -41,14 +43,27 @@ public static void copyScripts(File baseDirectory, int shellPort) throws IOExcep
File baseScriptFile = new File(scriptDir, Config.BASE_SCRIPT_FILENAME);
if (!baseScriptFile.exists()) {
- String scriptPrefix = "#!/bin/bash" + LS + "KNAPSACK_PORT=" + shellPort + LS;
+ StringBuilder sb = new StringBuilder();
+ sb.append("#!/bin/sh");
+ sb.append(LS);
+ sb.append("KNAPSACK_PORT=");
+ sb.append(shellPort);
+ sb.append(LS);
+ sb.append("KNAPSACK_JAR=");
+ sb.append(FSHelper.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
+ sb.append(LS);
+ sb.append("COMMAND=\"");
+ sb.append(command);
+ sb.append("\"");
+ sb.append(LS);
+
InputStream istream = Config.class.getResourceAsStream("/scripts/" + Config.BASE_SCRIPT_FILENAME);
if (istream == null)
throw new IOException("Jar resource does not exist: " + baseScriptFile);
FileOutputStream fos = new FileOutputStream(baseScriptFile);
- IOUtils.write(scriptPrefix, fos);
+ IOUtils.write(sb.toString(), fos);
IOUtils.copy(istream, fos);
fos.close();
@@ -64,6 +64,9 @@ public String executeCommand(String line) throws IOException {
} else if (line.length() > 0) {
IKnapsackCommand cmd = parser.parse(line);
+ if (cmd == null)
+ return "Unknown command: " + line;
+
if (hasHelpParam(cmd)) {
String rs = "";
@@ -111,7 +114,14 @@ public String executeCommand(String line) throws IOException {
}
+ /**
+ * @param cmd input command
+ * @return true if input command has the "-h" or "--help" option, false otherwise
+ */
private boolean hasHelpParam(IKnapsackCommand cmd) {
+ if (cmd == null || cmd.getArguments() == null)
+ return false;
+
return cmd.getArguments().contains("-h") || cmd.getArguments().contains("--help");
}
}
@@ -75,48 +75,54 @@
private ServerSocket socket;
private final Config config;
- public ConsoleSocketListener(Config config, int port, BundleContext context, LogService log, CommandParser parser) throws UnknownHostException, IOException, InvalidSyntaxException {
+ public ConsoleSocketListener(Config config, int port, BundleContext context, LogService log, CommandParser parser)
+ throws UnknownHostException, IOException, InvalidSyntaxException {
this.config = config;
this.parser = parser;
context.addServiceListener(parser, "(" + Constants.OBJECTCLASS + "=" + IKnapsackCommandSet.class.getName() + ")");
this.context = context;
this.log = log;
this.port = port;
}
-
+
public void run() {
running = true;
try {
if (commandProviderRegistration == null) {
- commandProviderRegistration = context.registerService(IKnapsackCommandSet.class.getName(), new BuiltinCommands(parser, log), null);
+ commandProviderRegistration = context.registerService(IKnapsackCommandSet.class.getName(),
+ new BuiltinCommands(parser, log), null);
}
this.socket = createServerSocket();
while (running) {
- Socket connection = socket.accept();
-
- if (!running)
- return;
-
- if (executor == null)
- executor = new CommandExecutor(parser);
-
- BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
- OutputStream out = connection.getOutputStream();
-
- String sl = in.readLine();
-
- if (sl != null) {
- String resp = executor.executeCommand(sl.trim());
-
- if (resp != null && resp.length() > 0) {
- out.write(resp.getBytes());
- if (!resp.endsWith(CRLF))
- out.write(CRLF.getBytes());
+ try {
+ Socket connection = socket.accept();
+
+ if (!running)
+ return;
+
+ if (executor == null)
+ executor = new CommandExecutor(parser);
+
+ BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+ OutputStream out = connection.getOutputStream();
+
+ String sl = in.readLine();
+
+ if (sl != null) {
+ String resp = executor.executeCommand(sl.trim());
+
+ if (resp != null && resp.length() > 0) {
+ out.write(resp.getBytes());
+ if (!resp.endsWith(CRLF))
+ out.write(CRLF.getBytes());
+ }
}
+
+ connection.close();
+ } catch (Exception e) {
+ log.log(LogService.LOG_ERROR, "An Error occurred while while processing command.", e);
}
-
- connection.close();
}
} catch (Exception e) {
log.log(LogService.LOG_ERROR, "An Error occurred while while processing command.", e);
@@ -128,7 +134,7 @@ public void run() {
try {
context.removeServiceListener(parser);
} catch (Exception e) {
- //Ignore unregistration errors.
+ // Ignore unregistration errors.
}
}
}
@@ -140,36 +146,36 @@ public void run() {
*/
private ServerSocket createServerSocket() throws UnknownHostException, IOException {
ServerSocket s;
-
+
if (config.getBoolean(Config.CONFIG_KEY_ACCEPT_ANY_HOST)) {
s = new ServerSocket(port, SERVER_BACKLOG_DEFAULT, null);
log.log(LogService.LOG_INFO, "Accepting socket connections from any host.");
} else {
- s = new ServerSocket(port, SERVER_BACKLOG_DEFAULT, InetAddress.getByAddress(new byte[]{127,0,0,1}));
- log.log(LogService.LOG_INFO, "Accepting socket connections from " + InetAddress.getByAddress(new byte[]{127,0,0,1}));
+ s = new ServerSocket(port, SERVER_BACKLOG_DEFAULT, InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 }));
+ log.log(LogService.LOG_INFO, "Accepting socket connections from " + InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 }));
}
-
+
Activator.logInfo("Created shell socket on port " + port);
return s;
}
-
+
/**
* @return the port that the socket listens on.
*/
public int getPort() {
return port;
}
-
+
/**
* Shutdown the listener. No new client connections will be accepted.
*/
- public void shutdown() {
+ public void shutdown() {
running = false;
this.interrupt();
if (socket != null)
try {
socket.close();
- } catch (IOException e) {
- }
+ } catch (IOException e) {
+ }
}
}
@@ -0,0 +1,47 @@
+package org.knapsack.shell;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+import org.apache.commons.io.IOUtils;
+
+/**
+ * Emulates the portion of netcat that reads from stdin and writes to a socket. This class
+ * is used instead of netcat due to different implementation APIs provided by various hosts.
+ *
+ * @author kgilmer
+ *
+ */
+public class Netcat {
+
+ /**
+ * @param args input arguments
+ * @throws UnknownHostException on name resolution error
+ * @throws IOException on IO exception
+ */
+ public static void main(String[] args) throws UnknownHostException, IOException {
+ //Check input parameters
+ if (args.length != 2) {
+ System.err.println("Invalid parameters");
+ System.out.println("Usage: Netcat [hostname] [port]");
+ System.exit(1);
+ }
+
+ //Load parameters
+ String host = args[0];
+ int port = Integer.parseInt(args[1]);
+
+ //Create and open socket
+ Socket socket = new Socket(host, port);
+ OutputStream out = socket.getOutputStream();
+ InputStream in = socket.getInputStream();
+
+ //Push, pull, close.
+ IOUtils.copy(System.in, out);
+ IOUtils.copy(in, System.out);
+ IOUtils.closeQuietly(socket);
+ }
+}
@@ -1,6 +1,3 @@
-#!/bin/sh
-# .knapsack-command.sh
-#
# This is the internal shell command used to pass 'native' commands to knapsack.
# It simply forwards the entire command line to knapsack, waits for a response,
# And prints the response back to the user.
@@ -10,4 +7,4 @@ if [ -z $KNAPSACK_PORT ]; then
exit 1
fi
-echo "`basename $0` $@" | nc -q 1 127.0.0.1 $KNAPSACK_PORT
+echo "`basename $0` $@" | $COMMAND 127.0.0.1 $KNAPSACK_PORT

0 comments on commit 4f78a84

Please sign in to comment.