{"_id":"54dcdf7bac05560d00335a15","parentDoc":null,"__v":38,"category":{"_id":"54d5635632d98b0d00384afe","__v":19,"version":"54d5635532d98b0d00384afb","pages":["54d5635732d98b0d00384b12","54d5635732d98b0d00384b13","54d5635732d98b0d00384b14","54d847c125e90a0d00db551f","54d995416a09052100a6b6d7","54d995534c49f40d00dff98a","54d9956b4c49f40d00dff98c","54dbce210e553a0d00dc1d09","54dcdf7bac05560d00335a15","54dd133c08f6b61700a691f7","54dd136108f6b61700a691fa","54dd27e0db3b362100131294","54dd2838ca1e5219007e9dcb","54dd28c8db3b362100131296","54deb1133a91410d001b1791","54deb134c4a3570d009d4a60","54dec5c0c2b4b70d009c3f05","54ded495e8e35a0d007551a0","54ded57d8e7b000d007c7f31","551447a90edb420d0050016e","551c4369ff892b3700d1c616"],"project":"54d53c7b23010a0d001aca0c","sync":{"url":"","isSync":false},"reference":false,"createdAt":"2015-02-06T23:44:18.708Z","from_sync":false,"order":4,"slug":"api-plugins","title":"API Plugins"},"project":"54d53c7b23010a0d001aca0c","user":"54cfa8e1a8a4fd0d00b7fd1d","version":{"_id":"54d5635532d98b0d00384afb","project":"54d53c7b23010a0d001aca0c","__v":10,"forked_from":"54d53c7c23010a0d001aca0f","createdAt":"2015-02-07T00:59:01.934Z","releaseDate":"2015-02-07T00:59:01.934Z","categories":["54d5635632d98b0d00384afc","54d5635632d98b0d00384afd","54d5635632d98b0d00384afe","54d5635632d98b0d00384aff","54d5635632d98b0d00384b00","54d5635632d98b0d00384b01","54d5635632d98b0d00384b02","54d652097e05890d006f153e","54dd1315ca1e5219007e9daa","54e21e2b22de1c230094b147","54e68e62a43fe13500db3879","54fa1d3fe7a0ba2f00306309","551c453a23a1ee190034d19a","551df586e52a0b23000c62b6","551f39be6886f8230055f02a","55a6720751457325000e4d97"],"is_deprecated":false,"is_hidden":false,"is_beta":true,"is_stable":true,"codename":"","version_clean":"0.1.0","version":"0.1"},"updates":["55a420871fd6fb0d00d6fab3","57518dbe7b01730e00c28d36"],"next":{"pages":[],"description":""},"createdAt":"2015-02-12T17:14:35.511Z","link_external":false,"link_url":"","githubsync":"","sync_unique":"","hidden":false,"api":{"results":{"codes":[]},"auth":"required","params":[],"url":""},"isReference":false,"order":8,"body":"All Cloud9 plugins are client side plugins. This means that your plugin's code is executed in the user's browser. The APIs that we provide allow you to interact with the user's workspace over the VFS (Virtual File System) protocol, providing a [Filesystem](doc:the-filesystem-api) , [Process](doc:the-process-api) and [Network](doc:the-network-api) API. \n\nFor some use cases these APIs don't suffice. For instance when you want to combine a good deal of logic with lots of file system or process interaction. Another use case is when you want to use code or binaries that are able to run in the browser. For these and other use cases Cloud9 offers an API that extends the VFS process, running in the user's workspace, with additional functionality.\n\n# Loading Remote Plugins\n\nYour regular Cloud9 plugin is responsible for loading a remote plugin. Remote plugins can be loaded from source code in a string, or from a file that is already in the workspace. See our guide about [dependency management](doc:adding-an-installer) on how to get your file(s) onto the workspace.\n\nThe following example loads a plugin from source.\n```javascript\nvar ext = imports.ext;\n\n// Load the remote plugin\next.loadRemotePlugin(\"api-name\", {\n    code: require(\"text!./server-plugin.js\")\n}, function(err, api) {\n\n    // Use the remote plugin\n    api.connect(5600, function(err, meta){\n        var stream = meta.stream;\n        stream.on(\"data\", function(chunk){\n            console.log(chunk);\n        });\n    });\n    \n});\n```\n\nOnce we get a reference to the remote plugin, we can start using it as any other api. In this case we get a readable stream object that we use to read data from. Lets take a look at our remote plugin.\n\nThe file in our example, `server-plugin.js` could look like this.\n```javascript\nmodule.exports = function (vfs, options, register) { \n    var Stream = require('stream');\n    var net = require('net');\n    var stream, server;\n    \n    function connect(port, callback) {\n        stream = new Stream();\n        stream.readable = true;\n        \n        server = net.createServer(function(reqStream) {\n            reqStream.on(\"data\", function(chunk) {\n                stream.emit(\"data\", chunk);\n            });\n        });\n        server.listen(port, \"localhost\", function(err) {\n            callback(err, !err && { stream: stream });\n        });\n    }\n    \n    register(null, {\n        connect: connect\n    });\n};\n```\n\nThe server plugin starts as a function that gets a reference to the vfs object as a first argument. The second argument contains the options object that was passed as an argument to `loadRemotePlugin()`. The last argument is the `register()` function that you already know from client side plugins.\n\nThe server plugin is executed in Node.js and thus all node's modules are available. In this example we use the `stream` and `net` module to open a tcp socket on localhost that allows an external client to connect to and send data to your client side plugin over a stream. The `connect()` function is made available for the client by setting it on the object that is passed too the `register()` function. \n\nYou can test if this works using telnet.\n```\n$ telnet localhost 5600\nTrying 127.0.0.1...\nConnected to localhost.\nEscape character is '^]'.\nHELLO\n```\n\nYou should receive \"HELLO\" in your client plugin.\n[block:callout]\n{\n  \"type\": \"success\",\n  \"title\": \"Always send a meta object\",\n  \"body\": \"Remote plugins are made available on the client by forwarding methods and events. In order for VFS to know what type of data you are sending make sure to return a `meta` object that contains a property called `stream`, `process`, `pty` or `api`. Any other properties will be sent as copies of that data. You cannot send functions or recursive objects.\"\n}\n[/block]\n# Error and Reconnect Handling\n\nLoading a remote plugin can fail. Common cases are when there's a syntax error in your code or if the client goes offline during loading the plugin.\n\nThe vfs process is restarted when a user is disconnected for longer than a few minutes. When this happens the server no longer has your remote plugin loaded and you'll need to load it again.\n\nThis example shows how to handle errors and reload a remote plugin when needed.\n```javascript\nvar c9 = imports.c9;\nvar ext = imports.ext;\n\nvar remote;\nfunction loadMyPlugin(){\n    ext.loadRemotePlugin(\"api-name\", {\n        file: c9.home + \"/.c9/lib/server-plugin.js\"\n    }, function(err, api) {\n        if (err) return console.error(err);\n    \n        remote = api;\n    });\n}\n\nloadMyPlugin();\n\nc9.on(\"connect\", loadMyPlugin);\nc9.on(\"disconnect\", function(){ remote = null });\n```\n\nIn the same way as before we load the remote plugin, but this time we are doing it in a function called `loadMyPlugin`. We simply ignore errors and log them to the console. This is fine because we also hook the `connect` event of the `c9` plugin. Whenever the client gets disconnected and reconnects this event is called and the remove plugin is (re)loaded again. \n\nIf somehow the plugin is still loaded we just get a reference to the same plugin. If you always want to load a new instance of your plugin set `redefine: true`.","excerpt":"","slug":"remote-plugins","type":"basic","title":"Remote Plugins"}
All Cloud9 plugins are client side plugins. This means that your plugin's code is executed in the user's browser. The APIs that we provide allow you to interact with the user's workspace over the VFS (Virtual File System) protocol, providing a [Filesystem](doc:the-filesystem-api) , [Process](doc:the-process-api) and [Network](doc:the-network-api) API. For some use cases these APIs don't suffice. For instance when you want to combine a good deal of logic with lots of file system or process interaction. Another use case is when you want to use code or binaries that are able to run in the browser. For these and other use cases Cloud9 offers an API that extends the VFS process, running in the user's workspace, with additional functionality. # Loading Remote Plugins Your regular Cloud9 plugin is responsible for loading a remote plugin. Remote plugins can be loaded from source code in a string, or from a file that is already in the workspace. See our guide about [dependency management](doc:adding-an-installer) on how to get your file(s) onto the workspace. The following example loads a plugin from source. ```javascript var ext = imports.ext; // Load the remote plugin ext.loadRemotePlugin("api-name", { code: require("text!./server-plugin.js") }, function(err, api) { // Use the remote plugin api.connect(5600, function(err, meta){ var stream = meta.stream; stream.on("data", function(chunk){ console.log(chunk); }); }); }); ``` Once we get a reference to the remote plugin, we can start using it as any other api. In this case we get a readable stream object that we use to read data from. Lets take a look at our remote plugin. The file in our example, `server-plugin.js` could look like this. ```javascript module.exports = function (vfs, options, register) { var Stream = require('stream'); var net = require('net'); var stream, server; function connect(port, callback) { stream = new Stream(); stream.readable = true; server = net.createServer(function(reqStream) { reqStream.on("data", function(chunk) { stream.emit("data", chunk); }); }); server.listen(port, "localhost", function(err) { callback(err, !err && { stream: stream }); }); } register(null, { connect: connect }); }; ``` The server plugin starts as a function that gets a reference to the vfs object as a first argument. The second argument contains the options object that was passed as an argument to `loadRemotePlugin()`. The last argument is the `register()` function that you already know from client side plugins. The server plugin is executed in Node.js and thus all node's modules are available. In this example we use the `stream` and `net` module to open a tcp socket on localhost that allows an external client to connect to and send data to your client side plugin over a stream. The `connect()` function is made available for the client by setting it on the object that is passed too the `register()` function. You can test if this works using telnet. ``` $ telnet localhost 5600 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. HELLO ``` You should receive "HELLO" in your client plugin. [block:callout] { "type": "success", "title": "Always send a meta object", "body": "Remote plugins are made available on the client by forwarding methods and events. In order for VFS to know what type of data you are sending make sure to return a `meta` object that contains a property called `stream`, `process`, `pty` or `api`. Any other properties will be sent as copies of that data. You cannot send functions or recursive objects." } [/block] # Error and Reconnect Handling Loading a remote plugin can fail. Common cases are when there's a syntax error in your code or if the client goes offline during loading the plugin. The vfs process is restarted when a user is disconnected for longer than a few minutes. When this happens the server no longer has your remote plugin loaded and you'll need to load it again. This example shows how to handle errors and reload a remote plugin when needed. ```javascript var c9 = imports.c9; var ext = imports.ext; var remote; function loadMyPlugin(){ ext.loadRemotePlugin("api-name", { file: c9.home + "/.c9/lib/server-plugin.js" }, function(err, api) { if (err) return console.error(err); remote = api; }); } loadMyPlugin(); c9.on("connect", loadMyPlugin); c9.on("disconnect", function(){ remote = null }); ``` In the same way as before we load the remote plugin, but this time we are doing it in a function called `loadMyPlugin`. We simply ignore errors and log them to the console. This is fine because we also hook the `connect` event of the `c9` plugin. Whenever the client gets disconnected and reconnects this event is called and the remove plugin is (re)loaded again. If somehow the plugin is still loaded we just get a reference to the same plugin. If you always want to load a new instance of your plugin set `redefine: true`.