diff --git a/configs/client-default.js b/configs/client-default.js index 789b64f7..8274dc08 100644 --- a/configs/client-default.js +++ b/configs/client-default.js @@ -618,7 +618,6 @@ module.exports = function(options) { }, { packagePath: "plugins/c9.cli.bridge/bridge", - port: 17123, startBridge: options.startBridge }, { diff --git a/plugins/c9.cli.bridge/bridge-client.js b/plugins/c9.cli.bridge/bridge-client.js index a283cf0c..edf8bb29 100644 --- a/plugins/c9.cli.bridge/bridge-client.js +++ b/plugins/c9.cli.bridge/bridge-client.js @@ -13,27 +13,59 @@ define(function(require, exports, module) { var c9 = imports.c9; var net = imports.net; + var JSONStream = require("./json-stream"); + /***** Initialization *****/ var plugin = new Plugin("Ajax.org", main.consumes); // var emit = plugin.getEmitter(); - var PORT = options.port || 17123; + var counter = 0; + var SOCKET = c9.home + "/.c9/bridge.socket"; /***** Methods *****/ function send(message, callback) { - net.connect(PORT, {}, function(err, stream) { + net.connect(SOCKET, {}, function(err, stream) { if (err) return callback(err); - stream.write(JSON.stringify(message)); - stream.end(); + var jstream = new JSONStream(stream); + var msgId = generateMessageId(); + var done; - callback(); + stream.write(JSON.stringify({ + id: msgId, + message: message + })); + + jstream.on("data", function(payload){ + if (payload.id == msgId && !done) { + done = true; + callback(null, payload.message); + stream.end(); + } + }); + + jstream.on("error", function(err){ + if (done) return; + callback(err); + done = true; + }); + + jstream.on("close", function(){ + if (done) return; + callback(new Error("No Response")); + done = true; + }) }); } + function generateMessageId(){ + // Use vfs token + return Math.random() + "-" + ++counter; + } + /***** Lifecycle *****/ plugin.on("load", function(){ diff --git a/plugins/c9.cli.bridge/bridge-service.js b/plugins/c9.cli.bridge/bridge-service.js index fd6543b9..f65bd0b8 100644 --- a/plugins/c9.cli.bridge/bridge-service.js +++ b/plugins/c9.cli.bridge/bridge-service.js @@ -1,37 +1,114 @@ module.exports = function (vfs, options, register) { + var stream; + + var net = require("net"); var Stream = require('stream'); - var stream, server; + + var SOCKET = process.env.HOME + "/.c9/bridge.socket"; + + function createListenClient(api){ + var client = net.connect(SOCKET, function(data){ + if (data) api.onData(data); + }); + client.setEncoding("utf8"); + client.unref(); + + client.on("data", function(data){ + if (data) api.onData(data); + }); + + client.on("error", function(err){ + if (err.code == "ECONNREFUSED") { + require("fs").unlink(SOCKET, function(){ + createListenServer(api); + }); + } + else + api.onError(err); + }); + + client.on("end", function(){ + createListenServer(api); + }); + + api.onConnect(client); + + api.disconnect = function(){ + client.end(); + }; + + return client; + } + + function createListenServer(api){ + var unixServer = net.createServer(function(client) { + client.setEncoding("utf8"); + + client.on("data", function(data){ + if (data) api.onData(data); + }); + + client.on("error", function(data){ + // console.error("ERROR", api.id, data); + }); + + api.onConnect(client); + }); + unixServer.listen(SOCKET); + + unixServer.on("error", function(err){ + if (err.code == "EADDRINUSE") { + createListenClient(api); + } + else + api.onError(err); + }); + + api.disconnect = function(){ + unixServer.close(); + }; + } register(null, { - connect: function (port, callback) { + connect: function (callback) { if (stream) return callback(null, { stream: stream }); - server = require('net').createServer(function(c) { - var buffer = ""; - c.on("data", function(chunk) { - buffer += chunk; - }); - c.on("end", function(){ - stream.emit("data", buffer); - }); - }); - server.on("error", function(err) { - callback(err); - }); - server.listen(port, process.env.OPENSHIFT_DIY_IP || "localhost", function(err) { - if (err) return callback(err); - callback(null, { stream: stream }); - }); - stream = new Stream(); stream.readable = true; + stream.writable = true; + + var client; + var sent = false; + var api = this.api = { + id: Math.random(), + onConnect: function(c){ + client = c; + if (sent) return; + + callback(null, { stream: stream }); + sent = true; + }, + onData: function(data){ + stream.emit("data", data); + }, + onError: function(err){ + stream.emit("error", err); + } + }; + + createListenServer(api); + + stream.on("data", function(data){ + if (client) client.write(data); + }); }, disconnect: function(){ - try { server && server.close(); } + try { this.api && this.api.disconnect(); } catch (e) {} + stream = null; - server = null; + delete this.api; } }); }; \ No newline at end of file diff --git a/plugins/c9.cli.bridge/bridge.js b/plugins/c9.cli.bridge/bridge.js index bf39c7bc..bd7baa47 100644 --- a/plugins/c9.cli.bridge/bridge.js +++ b/plugins/c9.cli.bridge/bridge.js @@ -1,4 +1,3 @@ - define(function(require, exports, module) { main.consumes = ["c9", "Plugin", "ext"]; main.provides = ["bridge"]; @@ -8,6 +7,8 @@ define(function(require, exports, module) { var Plugin = imports.Plugin; var c9 = imports.c9; var ext = imports.ext; + + var JSONStream = require("./json-stream"); /***** Initialization *****/ @@ -15,15 +16,10 @@ define(function(require, exports, module) { var emit = plugin.getEmitter(); var ENABLED = options.startBridge !== false; - var PORT = options.port || 17123; var stream, api; - var loaded = false; function load(){ - if (loaded) return; - loaded = true; - if (!ENABLED) return; ext.loadRemotePlugin("bridge", { @@ -35,58 +31,52 @@ define(function(require, exports, module) { api = remote; - api.connect(PORT, function(err, meta) { - if (err) { - loaded = false; + api.connect(function(err, meta) { + if (err) + return console.error(err); // this should never happen - if (err.code == "EADDRINUSE") { - console.warn("Another Application is using port " - + PORT + ". CLI client interface disabled. Restart Cloud9 to retry connecting."); - } - else - console.error(err); - - return; - } - - stream = meta.stream; - - stream.on("data", function(chunk) { - try { var message = JSON.parse(chunk); } - catch (e) { - setTimeout(function(){ - loaded = false; - load(); - }, 60000); - return; - } - emit("message", { message: message }); + stream = new JSONStream(meta.stream); + + stream.on("error", function(err) { + console.error(err); + }); + + stream.on("data", function(payload) { + var response = emit("message", { message: payload.message }); + + stream.write({ + id: payload.id, + message: response + }); }); stream.on("close", function(){ - loaded = false; + load(); }); }); }); - window.addEventListener("unload", unload); + window.addEventListener("unload", function(){ + api && api.disconnect(); + }); } - function unload() { - api && api.disconnect(); - api = stream = null; - loaded = false; + function write(json){ + if (!stream) return false; + stream.write(JSON.stringify(json)); + return true; } - + /***** Methods *****/ plugin.on("load", function(){ c9.on("connect", load, plugin); - c9.on("disconnect", unload, plugin); }); plugin.on("unload", function(){ api && api.disconnect(); + stream = null; + api = null; }); /***** Register and define API *****/ @@ -94,7 +84,12 @@ define(function(require, exports, module) { /** * Bridge To Communicate from CLI to IDE **/ - plugin.freezePublicAPI({ }); + plugin.freezePublicAPI({ + /** + * + */ + write: write + }); register(null, { bridge: plugin diff --git a/plugins/c9.cli.bridge/bridge_commands.js b/plugins/c9.cli.bridge/bridge_commands.js index 496af20a..723e2f6c 100644 --- a/plugins/c9.cli.bridge/bridge_commands.js +++ b/plugins/c9.cli.bridge/bridge_commands.js @@ -23,22 +23,20 @@ define(function(require, exports, module) { var BASEPATH = options.basePath; - var loaded = false; function load(){ - if (loaded) return; - loaded = true; - bridge.on("message", function(e) { var message = e.message; switch (message.type) { case "open": open(message); - break; + return true; case "ping": - break; + return true; + default: + return false; } - }); + }, plugin); } /***** Methods *****/ @@ -61,7 +59,7 @@ define(function(require, exports, module) { var node = favs.addFavorite(path); - tree.expand(path, function(err) { + tree.expand(path, function() { tree.select(node); //path || "/"); tree.scrollToSelection(); }); diff --git a/plugins/c9.cli.open/open.js b/plugins/c9.cli.open/open.js index 0ef32d02..3a22ca45 100755 --- a/plugins/c9.cli.open/open.js +++ b/plugins/c9.cli.open/open.js @@ -90,7 +90,7 @@ define(function(require, exports, module) { paths: paths }; - bridge.send(message, function cb(err) { + bridge.send(message, function cb(err, response) { if (err) { if (err.code == "ECONNREFUSED") { // Seems Cloud9 is not running, lets start it up @@ -111,6 +111,9 @@ define(function(require, exports, module) { console.log(err.message); } + if (response !== true) + console.log("Could not open ", paths); + process.exit(); // I don't get why this is needed }); } @@ -129,13 +132,16 @@ define(function(require, exports, module) { var timed = Date.now(); (function retry(){ - bridge.send({ type: "ping" }, function(err) { + bridge.send({ type: "ping" }, function(err, message) { if (!err) return callback(true); if (Date.now() - timed > 10000) return callback(false); + if (message !== true) + return callback(false); + setTimeout(retry, 100); }); })();