├── .gitignore ├── README.md ├── demo ├── commands │ └── mycommand.groovy ├── server │ ├── README.md │ └── server.js └── standalone │ ├── README.md │ └── conf.json ├── pom.xml └── src ├── main ├── assemblies │ └── mod.xml ├── java │ └── org │ │ └── vertx │ │ └── mods │ │ ├── CRaSHBusMod.java │ │ ├── Format.java │ │ ├── VertxCommand.java │ │ ├── VertxPluginLifeCycle.java │ │ └── VertxProcessContext.java └── resources │ ├── crash │ └── commands │ │ └── vertx │ │ ├── bus.groovy │ │ ├── module.groovy │ │ ├── sharedmap.groovy │ │ ├── verticle.groovy │ │ └── vertx.groovy │ └── mod.json └── test ├── java └── org │ └── vertx │ ├── java │ └── busmods │ │ └── CRaSHBusMod.java │ └── mods │ └── VertxTestCase.java ├── mods └── org.crashub.shell-v1.0 │ └── mod.json └── resources └── log4j.properties /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | target 3 | *.versionsBackup 4 | *.releaseBackup 5 | release.properties 6 | .idea 7 | *.iml 8 | derby.log 9 | nul 10 | .classpath 11 | .project 12 | 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The module mod-shell provides an embedded shell for Vert.x based on [CRaSH shell](https://github.com/crashub/crash). 2 | 3 | # Features 4 | 5 | * Extend Vertx with a command line interface 6 | * Script Vert.x 7 | * Write your own commands in Groovy or Java (more languages later...) 8 | * Execute commands from the event bus 9 | * Advanced Vert.x management 10 | * List Vert.x http and net servers 11 | * List Vert.x deployments 12 | * Interact with Vert.x shared maps 13 | * Hot deploy a module or a verticle 14 | * Hot undeploy a Vert.x deployment 15 | * Interact with the event bus: send or receive messages 16 | * Advanced shell features 17 | * Powerfull REPL 18 | * JVM commands : jdbc, thread, log, ... 19 | * Various plugin : mail, cron, ... 20 | * Writing custom commands is easy 21 | * Advance completion 22 | * SSH access 23 | 24 | # Documentation 25 | 26 | * This page 27 | * Vertx.shell [screencast](https://vimeo.com/67557338) 28 | * Official CRaSH [documentation](http://www.crashub.org/beta/reference.html) 29 | 30 | # Examples 31 | 32 | * [Server demo](https://github.com/crashub/mod-shell/tree/master/demo/server) : deploy vertx.shell as a module 33 | * [Standalone demo](https://github.com/crashub/mod-shell/tree/master/demo/standalone) : run vert.x shell as a module 34 | 35 | # Status 36 | 37 | * 2.0.7 stable for Vert.x 2.0.2-final 38 | * 2.1.0 stable for Vert.x 2.1.0 39 | * Published in Maven Central 40 | 41 | # Usage 42 | 43 | ## In your application 44 | 45 | Deploy the module org.crashub~vertx.shell in your verticle. 46 | 47 | ## Standalone 48 | 49 | echo '{"crash.auth": "simple","crash.auth.simple.username": "admin","crash.auth.simple.password": "admin","crash.ssh.port":2000}' > conf.json 50 | vertx runmod org.crashub~vertx.shell~2.1.0 -conf conf.json 51 | 52 | ## As a Vert.x module 53 | 54 | Deploy the org.crashub~vertx.shell~2.1.0 module from your verticle. 55 | 56 | # Configuration 57 | 58 | Default config: 59 | 60 | { 61 | "crash.auth": "simple", 62 | "crash.auth.simple.username": "admin", 63 | "crash.auth.simple.password": "admin", 64 | "crash.ssh.port": "2000" 65 | } 66 | 67 | 68 | Configuration is mostly based on CRaSH configuration explained in CRaSH [documentation](http://www.crashub.org/beta/reference.html): 69 | 70 | # Walkthrough 71 | 72 | ## Install 73 | 74 | Install the shell module: 75 | 76 | vertx install org.crashub~vertx.shell~2.1.0 77 | echo '{"crash.auth": "simple","crash.auth.simple.username": "admin","crash.auth.simple.password": "admin"}' > conf.json 78 | vertx runmod org.crashub~vertx.shell~2.1.0 -conf conf.json 79 | 80 | ## Receive and send messages 81 | 82 | Create a message subscriber 83 | 84 | (! 576)-> ssh -p 2000 admin@localhost 85 | admin@localhost's password: 86 | Welcome to Juliens-MacBook-Pro.local + ! 87 | It is Sat Jan 12 15:47:50 CET 2013 now 88 | % bus subscribe the_address 89 | 90 | Log in with another console and send a message on the_address: 91 | 92 | (! 501)-> ssh -p 2000 admin@localhost 93 | admin@localhost's password: 94 | Welcome to Juliens-MacBook-Pro.local + ! 95 | It is Sat Jan 12 15:48:52 CET 2013 now 96 | % bus send the_address Hello 97 | 98 | 99 | ## Send an email with the Mailer module 100 | 101 | Create the file server.js: 102 | 103 | var container = require('vertx/container'); 104 | container.deployModule("io.vertx~mod-mailer~2.0.3-beta2", { 105 | "address": "test.my_mailer", 106 | "host": "smtp.googlemail.com", 107 | "port": 465, 108 | "ssl": true, 109 | "auth": true, 110 | "username": "username", 111 | "password": "password" 112 | }); 113 | container.deployModule("org.crashub~vertx.shell~2.1.0", { 114 | "cmd": ".", 115 | "crash.auth": "simple", 116 | "crash.auth.simple.username": "admin", 117 | "crash.auth.simple.password": "admin", 118 | "crash.ssh.port": 2000 119 | }); 120 | 121 | Run Vert.x: 122 | 123 | vertx run server.js 124 | 125 | Use the shell: 126 | 127 | (! 569)-> ssh -p 2000 admin@localhost 128 | admin@localhost's password: 129 | Welcome to Juliens-MacBook-Pro.local + ! 130 | It is Sat Jan 12 15:28:37 CET 2013 now 131 | % bus send -f JSON test.my_mailer {"from":"julien.viet@gmail.com","to":"julien@julienviet.com","subject":"test","body":"sent from Vert.x"} 132 | 133 | ## Interact with the JDBC module 134 | 135 | Create the server.js file: 136 | 137 | var container = require('vertx/container'); 138 | container.deployModule("com.bloidonia~mod-jdbc-persistor~2.0.3-beta5", { 139 | "address" : "db", 140 | "driver" : "org.hsqldb.jdbcDriver", 141 | "url" : "jdbc:hsqldb:mem:test", 142 | "username" : "", 143 | "password" : "" 144 | }); 145 | container.deployModule("org.crashub~vertx.shell~2.1.0", { 146 | "cmd": ".", 147 | "crash.auth": "simple", 148 | "crash.auth.simple.username": "admin", 149 | "crash.auth.simple.password": "admin", 150 | "crash.ssh.port": 2000 151 | }); 152 | 153 | Copy the HSLQDB jar in your $VERTX_HOME/lib. 154 | 155 | Run Vert.x: 156 | 157 | vertx run server.js 158 | 159 | Use the JDBC module: 160 | 161 | (! 575)-> ssh -p 2000 admin@localhost 162 | admin@localhost's password: 163 | It is Sat Jan 12 15:44:58 CET 2013 now 164 | % bus send -f JSON -r db { "action": "select", "stmt": "SELECT * FROM INFORMATION_SCHEMA.SYSTEM_USERS" } 165 | {"result":[{"INITIAL_SCHEMA":null,"ADMIN":true,"USER_NAME":""}],"status":"ok"} 166 | 167 | The -r option stands for reply and tell the command to wait and block until a reply is provided after sending the message. 168 | This is useful with the jdbc module as it sends the result of statement in a response. 169 | 170 | ## Request executor 171 | 172 | CRaSH registers an event handler to the "crash.execute" address which process shell requests. This feature is experimental 173 | at the moment (feedback welcome). 174 | 175 | The format of the event message is Json and contains the mandatory _requests_ String array field. 176 | 177 | % bus publish --format JSON crash.execute {"requests":["help"]} 178 | 179 | The optional _replyTo_ field can be used, when specified the handler sends response events to the _replyTo_ address: 180 | 181 | % bus publish --format JSON crash.execute {"requests":["help"],"replyTo":"screen"} 182 | 183 | The _vertx execute_ command can be used to make this easier, it will _publish_ the message: 184 | 185 | % vertx execute help 186 | 187 | Special care should be ported to the request arguments whitespaces: 188 | 189 | % vertx execute "thread ls" 190 | 191 | Several requests can be specified, when a requests ends, the response is sent and the next request is processed. Therefore 192 | the execution order is sequential. 193 | 194 | % vertx execute "repl groovy" "1+1" 195 | 196 | The _bus subscribe_ command can be used to receive the responses, of course this should be done in another 197 | terminal: 198 | 199 | % bus subscribe screen 200 | 201 | ## Creating custom commands in Groovy or Java 202 | 203 | Pretty much like the first example, however we add the current directory under the "cmd" key in the configuration: 204 | 205 | echo '{"cmd":".","crash.auth": "simple","crash.auth.simple.username": "admin","crash.auth.simple.password": "admin"}' > conf.json 206 | 207 | Edit hello.groovy 208 | 209 | return "Hello from ${context.attributes.vertx}" 210 | 211 | Run Vert.x: 212 | 213 | vertx runmod org.crashub~vertx.shell~2.1.0 -conf conf.json 214 | 215 | Use the hello command: 216 | 217 | (! 505)-> ssh -p 2000 admin@localhost 218 | admin@localhost's password: 219 | Welcome to Juliens-MacBook-Pro.local + ! 220 | It is Sat Jan 12 16:41:50 CET 2013 now 221 | % hello 222 | Hello from org.vertx.java.core.impl.DefaultVertx@5e6b6477 223 | 224 | Inside a Groovy command the current Vertx and Container objects are available under 225 | 226 | def vertx = context.attributes.vertx 227 | def container = context.attributes.container 228 | 229 | For more information about CRaSH commands please read the [documentation](http://www.crashub.org/beta/reference.html) 230 | 231 | Commands located in _cmd_ are live reloaded. Note that commands located under _crash/commands_ are loaded only once as they are 232 | considered as classpath commands (and classpath is not supposed to change). 233 | 234 | # Vert.x commands 235 | 236 | The module embeds an SSH server to an embedded shell in Vert.x . CRaSH comes out of the box with a useful bunch of commands, 237 | however the Vert.x integration provides commands for Vert.x. You can look or modify the existing commands in the mods directory 238 | ($VERTX_MODS) as resources under $VERTX_MODS/org.crashub~vertx.shell~2.1.0/crash/commands/vertx : 239 | 240 | (! 561)-> ls -l $VERTX_MODS/org.crashub~vertx.shell~2.1.0/crash/commands/vertx 241 | total 40 242 | -rw-r--r-- 1 julien staff 3463 Jan 12 16:17 bus.groovy 243 | -rw-r--r-- 1 julien staff 1213 Jan 12 16:17 module.groovy 244 | -rw-r--r-- 1 julien staff 2212 Jan 12 16:17 sharedmap.groovy 245 | -rw-r--r-- 1 julien staff 2016 Jan 12 16:17 verticle.groovy 246 | -rw-r--r-- 1 julien staff 3150 Jan 12 16:17 vertx.groovy 247 | 248 | You can modify the existing commands if you like or add new commands, such commands will be visible each time the module 249 | is deployed. 250 | 251 | ## help command 252 | 253 | (! 566)-> ssh -p 2000 admin@localhost 254 | admin@localhost's password: 255 | It is Sat Jan 12 15:09:46 CET 2013 now 256 | % help 257 | Try one of these commands with the -h or --help switch: 258 | 259 | NAME DESCRIPTION 260 | bus interact with the vert.x event bus 261 | module interact with vert.x modules 262 | sharedmap interact with the vert.x shared map 263 | verticle interact with vert.x verticles 264 | vertx interact with vert.x 265 | 266 | ## vertx command 267 | 268 | usage: vertx [-h | --help] COMMAND [ARGS] 269 | 270 | The most commonly used vertx commands are: 271 | execute execute a shell request 272 | net list existing net servers 273 | config display vert.x config 274 | undeploy undeploy a deployment 275 | deployments list existing deployments 276 | deployment Provide more info about an existing deployment 277 | http list existing http servers 278 | 279 | 280 | ## module command 281 | 282 | usage: module [-h | --help] COMMAND [ARGS] 283 | 284 | The most commonly used module commands are: 285 | deploy deploy a module 286 | 287 | ## sharedmap command 288 | 289 | usage: sharedmap [-h | --help] COMMAND [ARGS] 290 | 291 | The most commonly used sharedmap commands are: 292 | get get a value 293 | put put a value 294 | clear clear a map 295 | destroy destroy a shared map 296 | keys list content of a map 297 | rm remove a value 298 | 299 | ## verticle command 300 | 301 | usage: verticle [-h | --help] COMMAND [ARGS] 302 | 303 | The most commonly used verticle commands are: 304 | deploy deploy a verticle 305 | 306 | ## bus command 307 | 308 | usage: bus [-h | --help] COMMAND [ARGS] 309 | 310 | The most commonly used bus commands are: 311 | publish publish a JSON object as a message 312 | send send a message on the bus 313 | subscribe read message from the bus 314 | -------------------------------------------------------------------------------- /demo/commands/mycommand.groovy: -------------------------------------------------------------------------------- 1 | return "HELLO WORLD" -------------------------------------------------------------------------------- /demo/server/README.md: -------------------------------------------------------------------------------- 1 | Run with 2 | 3 | vertx run server.js 4 | 5 | Login (pass=admin) with 6 | 7 | ssh -p 2000 admin@localhost 8 | -------------------------------------------------------------------------------- /demo/server/server.js: -------------------------------------------------------------------------------- 1 | // A small server setup with mod-shell 2 | 3 | var vertx = require('vertx'); 4 | var console = require('vertx/console'); 5 | var container = require('vertx/container'); 6 | 7 | // Shared map 8 | var map = vertx.getMap('demo.mymap'); 9 | map.put('some-key', 'some-value'); 10 | 11 | // Deploy mod-shell and additional modules 12 | container.deployModule("com.bloidonia~mod-jdbc-persistor~2.0.0-beta5", { 13 | "address" : "db", 14 | "driver" : "org.hsqldb.jdbcDriver", 15 | "url" : "jdbc:hsqldb:mem:test", 16 | "username" : "", 17 | "password" : "" 18 | }); 19 | container.deployModule("io.vertx~mod-mailer~2.0.0-beta2", { 20 | "address": "test.my_mailer", 21 | "host": "smtp.googlemail.com", 22 | "port": 465, 23 | "ssl": true, 24 | "auth": true, 25 | "username": "username", 26 | "password": "password" 27 | }); 28 | container.deployModule("org.crashub~vertx.shell~2.1.0", { 29 | "cmd": "../commands", 30 | "crash.auth": "simple", 31 | "crash.auth.simple.username": "admin", 32 | "crash.auth.simple.password": "admin", 33 | "crash.ssh.port": 2000 34 | }); 35 | 36 | -------------------------------------------------------------------------------- /demo/standalone/README.md: -------------------------------------------------------------------------------- 1 | Run with 2 | 3 | vertx runmod org.crashub~vertx.shell~2.1.0 -conf conf.json 4 | 5 | Login (pass=admin) with 6 | 7 | ssh -p 2000 admin@localhost 8 | -------------------------------------------------------------------------------- /demo/standalone/conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "../commands", 3 | "crash.auth": "simple", 4 | "crash.auth.simple.username": "admin", 5 | "crash.auth.simple.password": "admin", 6 | "crash.ssh.port":2000} 7 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | org.crashub 4 | vertx.shell 5 | jar 6 | 2.1.1-SNAPSHOT 7 | 8 | org.sonatype.oss 9 | oss-parent 10 | 7 11 | 12 | Shell module for Vert.x 13 | A Vert.x module providing remote shell access based on CRaSH shell 14 | http://maven.apache.org 15 | 16 | Crashub 17 | http://www.crashub.org 18 | 19 | 20 | 21 | LGPL, version 2.1 22 | http://www.opensource.org/licenses/lgpl-license.php 23 | 24 | 25 | 26 | scm:git:git://github.com/crashub/mod-shell.git 27 | scm:git:ssh://git@github.com/crashub/mod-shell.git 28 | http://www.crashub.org 29 | 30 | 31 | 32 | julien.viet 33 | Julien Viet 34 | julien.viet@exoplatform.com 35 | 36 | Owner 37 | 38 | 39 | 40 | 41 | 42 | 43 | false 44 | -Psign-artifacts 45 | false 46 | true 47 | 48 | 49 | 7 50 | 7 51 | 52 | 53 | UTF-8 54 | 55 | 56 | true 57 | 58 | 1.3.0 59 | 2.1 60 | 61 | 62 | 63 | 64 | 65 | io.vertx 66 | vertx-core 67 | ${vertx.version} 68 | 69 | 70 | io.vertx 71 | vertx-platform 72 | ${vertx.version} 73 | 74 | 75 | 76 | org.crashub 77 | crash.cli 78 | ${crash.version} 79 | 80 | 81 | org.crashub 82 | crash.shell 83 | ${crash.version} 84 | 85 | 86 | org.codehaus.groovy 87 | groovy-all 88 | 1.8.9 89 | 90 | 91 | junit 92 | junit 93 | 94 | 95 | 96 | 97 | 98 | 99 | org.crashub 100 | crash.connectors.ssh 101 | ${crash.version} 102 | 103 | 104 | org.apache.mina 105 | mina-core 106 | 2.0.7 107 | 108 | 109 | org.apache.sshd 110 | sshd-core 111 | 0.11.0 112 | 113 | 114 | org.apache.sshd 115 | sshd-pam 116 | 0.11.0 117 | 118 | 119 | org.bouncycastle 120 | bcprov-jdk15on 121 | 1.49 122 | 123 | 124 | org.bouncycastle 125 | bcpkix-jdk15on 126 | 1.49 127 | 128 | 129 | org.slf4j 130 | slf4j-api 131 | 1.7.2 132 | 133 | 134 | org.slf4j 135 | slf4j-jdk14 136 | 1.7.2 137 | 138 | 139 | 140 | junit 141 | junit 142 | 4.10 143 | test 144 | 145 | 146 | io.vertx 147 | testtools 148 | 2.0.0-final 149 | test 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | org.apache.maven.plugins 158 | maven-source-plugin 159 | 2.1.2 160 | 161 | 162 | org.apache.maven.plugins 163 | maven-release-plugin 164 | 2.1 165 | 166 | 167 | org.apache.maven.plugins 168 | maven-javadoc-plugin 169 | 2.8 170 | 171 | 172 | org.apache.maven.plugins 173 | maven-compiler-plugin 174 | 2.3.2 175 | 176 | 177 | org.apache.maven.plugins 178 | maven-jar-plugin 179 | 2.3.1 180 | 181 | 182 | org.apache.maven.plugins 183 | maven-install-plugin 184 | 2.3.1 185 | 186 | 187 | org.apache.maven.plugins 188 | maven-deploy-plugin 189 | 2.5 190 | 191 | 192 | org.apache.maven.plugins 193 | maven-clean-plugin 194 | 2.4.1 195 | 196 | 197 | org.apache.maven.plugins 198 | maven-surefire-plugin 199 | 2.12 200 | 201 | 202 | src/test/mods 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | maven-assembly-plugin 213 | 2.3 214 | 215 | true 216 | 217 | 218 | 219 | mod 220 | 221 | single 222 | 223 | package 224 | 225 | src/main/assemblies/mod.xml 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | org.apache.maven.plugins 234 | maven-source-plugin 235 | 236 | 237 | attach-sources 238 | 239 | jar 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | org.apache.maven.plugins 248 | maven-release-plugin 249 | 250 | forked-path 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | sign-artifacts 260 | 261 | 262 | 263 | 264 | org.apache.maven.plugins 265 | maven-gpg-plugin 266 | 267 | 268 | 269 | sign 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | -------------------------------------------------------------------------------- /src/main/assemblies/mod.xml: -------------------------------------------------------------------------------- 1 | 4 | mod 5 | 6 | zip 7 | 8 | 9 | true 10 | / 11 | 12 | 13 | 14 | ${project.build.outputDirectory} 15 | / 16 | 17 | 18 | 19 | 20 | 21 | 22 | org.crashub:crash.cli 23 | org.crashub:crash.shell 24 | org.crashub:crash.connectors.ssh 25 | org.apache.mina:mina-core 26 | org.apache.sshd:sshd-core 27 | org.apache.sshd:sshd-pam 28 | org.bouncycastle:bcprov-jdk15on 29 | org.bouncycastle:bcpkix-jdk15on 30 | org.slf4j:slf4j-api 31 | org.slf4j:slf4j-jdk14 32 | org.codehaus.groovy:groovy-all 33 | 34 | false 35 | true 36 | false 37 | /lib 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/main/java/org/vertx/mods/CRaSHBusMod.java: -------------------------------------------------------------------------------- 1 | package org.vertx.mods; 2 | 3 | import org.crsh.shell.Shell; 4 | import org.crsh.shell.ShellFactory; 5 | import org.vertx.java.busmods.BusModBase; 6 | import org.vertx.java.core.Handler; 7 | import org.vertx.java.core.eventbus.Message; 8 | import org.vertx.java.core.json.JsonArray; 9 | import org.vertx.java.core.json.JsonObject; 10 | 11 | import java.util.LinkedList; 12 | 13 | /** @author Julien Viet */ 14 | public class CRaSHBusMod extends BusModBase { 15 | 16 | /** . */ 17 | private VertxPluginLifeCycle lifeCycle; 18 | 19 | @Override 20 | public void start() { 21 | try { 22 | ClassLoader loader = Thread.currentThread().getContextClassLoader(); 23 | lifeCycle = new VertxPluginLifeCycle(loader, this); 24 | lifeCycle.start(); 25 | getVertx().eventBus().registerHandler("crash.execute", new Handler>() { 26 | @Override 27 | public void handle(Message event) { 28 | JsonArray requestsArray = event.body().getArray("requests"); 29 | String replyTo = event.body().getString("replyTo"); 30 | LinkedList requests = new LinkedList<>(); 31 | for (Object o : requestsArray) { 32 | requests.add(o.toString()); 33 | } 34 | ShellFactory factory = lifeCycle.getContext().getPlugin(ShellFactory.class); 35 | Shell shell = factory.create(null); 36 | VertxProcessContext context = new VertxProcessContext( 37 | getVertx().eventBus(), 38 | shell, 39 | requests, 40 | replyTo); 41 | context.run(); 42 | } 43 | }); 44 | } 45 | catch (Exception e) { 46 | throw new RuntimeException("Could not start mod", e); 47 | } 48 | } 49 | 50 | @Override 51 | public void stop() { 52 | if (lifeCycle != null) { 53 | lifeCycle.stop(); 54 | lifeCycle = null; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/org/vertx/mods/Format.java: -------------------------------------------------------------------------------- 1 | package org.vertx.mods; 2 | 3 | import org.vertx.java.core.Handler; 4 | import org.vertx.java.core.eventbus.EventBus; 5 | import org.vertx.java.core.eventbus.Message; 6 | import org.vertx.java.core.json.JsonObject; 7 | 8 | /** @author Julien Viet */ 9 | public enum Format { 10 | 11 | JSON() { 12 | 13 | @Override 14 | public void publish(EventBus bus, String address, String value) { 15 | JsonObject json = VertxCommand.parseJson(value); 16 | bus.publish(address, json); 17 | } 18 | 19 | @Override 20 | public void send(EventBus bus, String address, String value, final Handler> replyHandler) { 21 | JsonObject json = VertxCommand.parseJson(value); 22 | if (replyHandler != null) { 23 | bus.send(address, json, new Handler>() { 24 | public void handle(Message event) { 25 | replyHandler.handle(event); 26 | } 27 | }); 28 | } else { 29 | bus.send(address, json); 30 | } 31 | } 32 | }, 33 | 34 | STRING() { 35 | 36 | @Override 37 | public void publish(EventBus bus, String address, String value) { 38 | bus.publish(address, value); 39 | } 40 | 41 | @Override 42 | public void send(EventBus bus, String address, String value, final Handler> replyHandler) { 43 | if (replyHandler != null) { 44 | bus.send(address, value, new Handler>() { 45 | public void handle(Message event) { 46 | replyHandler.handle(event); 47 | } 48 | }); 49 | } else { 50 | bus.send(address, value); 51 | } 52 | } 53 | }; 54 | 55 | public abstract void publish(EventBus bus, String address, String value); 56 | 57 | public abstract void send(EventBus bus, String address, String value, Handler> replyHandler); 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/org/vertx/mods/VertxCommand.java: -------------------------------------------------------------------------------- 1 | package org.vertx.mods; 2 | 3 | import org.crsh.command.ScriptException; 4 | import org.crsh.groovy.GroovyCommand; 5 | import org.vertx.java.core.Vertx; 6 | import org.vertx.java.core.json.DecodeException; 7 | import org.vertx.java.core.json.JsonObject; 8 | import org.vertx.java.platform.Container; 9 | import org.vertx.java.platform.impl.DefaultContainer; 10 | import org.vertx.java.platform.impl.DefaultPlatformManager; 11 | import org.vertx.java.platform.impl.Deployment; 12 | import org.vertx.java.platform.impl.PlatformManagerInternal; 13 | 14 | import java.lang.reflect.Field; 15 | import java.util.Collections; 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | /** @author Julien Viet */ 20 | public class VertxCommand extends GroovyCommand { 21 | 22 | protected final Vertx getVertx() { 23 | return (Vertx)context.getAttributes().get("vertx"); 24 | } 25 | 26 | protected final Container getContainer() { 27 | return (Container)context.getAttributes().get("container"); 28 | } 29 | 30 | protected final PlatformManagerInternal getManager() { 31 | try { 32 | Container container = getContainer(); 33 | Field f = DefaultContainer.class.getDeclaredField("mgr"); 34 | f.setAccessible(true); 35 | return (PlatformManagerInternal)f.get(container); 36 | } 37 | catch (Exception e) { 38 | e.printStackTrace(); 39 | throw new ScriptException("Could not access verticle manager"); 40 | } 41 | } 42 | 43 | protected final Map getDeployments() { 44 | try { 45 | PlatformManagerInternal mgr = getManager(); 46 | Field d = DefaultPlatformManager.class.getDeclaredField("deployments"); 47 | d.setAccessible(true); 48 | return Collections.unmodifiableMap((Map)d.get(mgr)); 49 | } 50 | catch (Exception e) { 51 | throw new ScriptException("Could not access deployments"); 52 | } 53 | } 54 | 55 | public static String join(List parts) { 56 | StringBuilder sb = new StringBuilder(); 57 | for (String part : parts) { 58 | sb.append(part); 59 | } 60 | return sb.toString(); 61 | } 62 | 63 | public static JsonObject parseJson(List parts) throws ScriptException { 64 | return parseJson(join(parts)); 65 | } 66 | 67 | public static JsonObject parseJson(String s) throws ScriptException { 68 | try { 69 | return new JsonObject(s); 70 | } 71 | catch (DecodeException ignore) { 72 | throw new ScriptException("Invalid JSON:" + s); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/org/vertx/mods/VertxPluginLifeCycle.java: -------------------------------------------------------------------------------- 1 | package org.vertx.mods; 2 | 3 | import org.crsh.plugin.PluginContext; 4 | import org.crsh.plugin.PluginLifeCycle; 5 | import org.crsh.plugin.ServiceLoaderDiscovery; 6 | import org.crsh.vfs.FS; 7 | import org.crsh.vfs.Path; 8 | import org.vertx.java.core.json.JsonArray; 9 | import org.vertx.java.core.json.JsonObject; 10 | import org.vertx.java.platform.Verticle; 11 | 12 | import java.io.File; 13 | import java.util.Collections; 14 | import java.util.HashMap; 15 | import java.util.Properties; 16 | 17 | /** @author Julien Viet */ 18 | class VertxPluginLifeCycle extends PluginLifeCycle { 19 | 20 | /** . */ 21 | private final ClassLoader loader; 22 | 23 | /** . */ 24 | private final Verticle verticle; 25 | 26 | /** . */ 27 | private final PluginContext context; 28 | 29 | VertxPluginLifeCycle(ClassLoader loader, Verticle verticle) throws Exception { 30 | 31 | // 32 | HashMap attributes = new HashMap(); 33 | attributes.put("vertx", verticle.getVertx()); 34 | attributes.put("container", verticle.getContainer()); 35 | 36 | // 37 | JsonObject verticleConfig = verticle.getContainer().config(); 38 | 39 | // Build configuration 40 | Properties config = new Properties(); 41 | for (String s : verticleConfig.getFieldNames()) { 42 | if (s.startsWith("crash.")) { 43 | String value = "" + verticleConfig.getField(s); 44 | config.put(s, value); 45 | } 46 | } 47 | setConfig(config); 48 | 49 | // 50 | FS confFS = new FS(); 51 | 52 | // 53 | FS cmdFS = new FS(); 54 | cmdFS.mount(loader, Path.get("/crash/commands/")); 55 | Object o = verticleConfig.getField("cmd"); 56 | if (o instanceof String) { 57 | cmdFS.mount(new File((String)o)); 58 | } else if (o instanceof JsonArray) { 59 | JsonArray array = (JsonArray)o; 60 | for (Object e : array) { 61 | if (e instanceof String) { 62 | cmdFS.mount(new File((String)e)); 63 | } 64 | } 65 | } 66 | 67 | // 68 | PluginContext context = new PluginContext( 69 | new ServiceLoaderDiscovery(loader), 70 | Collections.unmodifiableMap(attributes), 71 | cmdFS, 72 | confFS, 73 | loader); 74 | 75 | // 76 | this.loader = loader; 77 | this.verticle = verticle; 78 | this.context = context; 79 | } 80 | 81 | void start() { 82 | context.refresh(); 83 | start(context); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/org/vertx/mods/VertxProcessContext.java: -------------------------------------------------------------------------------- 1 | package org.vertx.mods; 2 | 3 | import org.crsh.shell.Shell; 4 | import org.crsh.shell.ShellProcess; 5 | import org.crsh.shell.ShellProcessContext; 6 | import org.crsh.shell.ShellResponse; 7 | import org.crsh.text.ScreenBuffer; 8 | import org.crsh.text.Screenable; 9 | import org.crsh.text.Style; 10 | import org.crsh.text.Format; 11 | import org.vertx.java.core.eventbus.EventBus; 12 | 13 | import java.io.IOException; 14 | import java.util.LinkedList; 15 | 16 | /** 17 | * @author Julien Viet 18 | */ 19 | public class VertxProcessContext implements ShellProcessContext { 20 | 21 | /** . */ 22 | private final ScreenBuffer screen = new ScreenBuffer(); 23 | 24 | /** . */ 25 | private final LinkedList requests; 26 | 27 | /** . */ 28 | private final Shell shell; 29 | 30 | /** . */ 31 | private final String replyTo; 32 | 33 | /** . */ 34 | private final EventBus bus; 35 | 36 | public VertxProcessContext(EventBus bus, Shell shell, LinkedList requests, String replyTo) { 37 | this.bus = bus; 38 | this.replyTo = replyTo; 39 | this.shell = shell; 40 | this.requests = requests; 41 | } 42 | 43 | public void run() { 44 | if (requests.size() > 0) { 45 | ShellProcess process = shell.createProcess(requests.peekFirst()); 46 | process.execute(this); 47 | } 48 | } 49 | 50 | @Override 51 | public void end(ShellResponse response) { 52 | 53 | // Remove what we executed 54 | String request = requests.removeFirst(); 55 | 56 | // 57 | if (replyTo != null) { 58 | try { 59 | // For now render to text 60 | StringBuilder buffer = new StringBuilder(); 61 | 62 | // 63 | buffer.append(request).append(":\n"); 64 | 65 | // For now render to text 66 | screen.format(Format.TEXT, buffer); 67 | 68 | // Append response message if any 69 | String msg = response.getMessage(); 70 | if (msg != null) { 71 | buffer.append(msg); 72 | } 73 | 74 | // Publish message 75 | bus.publish(replyTo, buffer.toString()); 76 | } 77 | catch (IOException e) { 78 | e.printStackTrace(); 79 | } 80 | } 81 | 82 | // Should be a "reset" but for now we don't care about style 83 | screen.clear(); 84 | 85 | // Execute next if any 86 | run(); 87 | } 88 | 89 | @Override 90 | public boolean takeAlternateBuffer() throws IOException { 91 | return false; 92 | } 93 | 94 | @Override 95 | public boolean releaseAlternateBuffer() throws IOException { 96 | return false; 97 | } 98 | 99 | @Override 100 | public String getProperty(String propertyName) { 101 | return null; 102 | } 103 | 104 | @Override 105 | public String readLine(String msg, boolean echo) throws IOException, InterruptedException, IllegalStateException { 106 | return null; 107 | } 108 | 109 | @Override 110 | public int getWidth() { 111 | return 80; 112 | } 113 | 114 | @Override 115 | public int getHeight() { 116 | return 40; 117 | } 118 | 119 | @Override 120 | public Appendable append(char c) throws IOException { 121 | screen.append(c); 122 | return this; 123 | } 124 | 125 | @Override 126 | public Appendable append(CharSequence s) throws IOException { 127 | screen.append(s); 128 | return this; 129 | } 130 | 131 | @Override 132 | public Appendable append(CharSequence csq, int start, int end) throws IOException { 133 | screen.append(csq, start, end); 134 | return null; 135 | } 136 | 137 | @Override 138 | public Screenable append(Style style) throws IOException { 139 | screen.append(style); 140 | return this; 141 | } 142 | 143 | @Override 144 | public Screenable cls() throws IOException { 145 | screen.cls(); 146 | return this; 147 | } 148 | 149 | @Override 150 | public void flush() throws IOException { 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/main/resources/crash/commands/vertx/bus.groovy: -------------------------------------------------------------------------------- 1 | package crash.commands.vertx 2 | 3 | import org.crsh.cli.Argument 4 | import org.crsh.cli.Command 5 | import org.crsh.cli.Option 6 | import org.crsh.cli.Required 7 | import org.crsh.cli.Usage 8 | import org.vertx.java.core.Handler 9 | import org.vertx.java.core.eventbus.EventBus 10 | import org.vertx.java.core.eventbus.Message 11 | import org.vertx.mods.Format 12 | import org.vertx.mods.VertxCommand 13 | 14 | import java.util.concurrent.atomic.AtomicReference 15 | 16 | @Usage("interact with the vert.x event bus") 17 | public class bus extends VertxCommand { 18 | 19 | @Usage("send a message on the bus") 20 | @Command 21 | public void send( 22 | @Usage("the address to send to") 23 | @Argument(name = "address") 24 | @Required String address, 25 | @Usage("the message format") 26 | @Option(names = ["f","format"]) 27 | Format format, 28 | @Usage("wait for a reply and publish it on the console") 29 | @Option(names= ["r","reply"]) 30 | Boolean reply, 31 | @Usage("the message") 32 | @Argument(name = "message", unquote = false) 33 | @Required List parts) { 34 | String value = join(parts); 35 | EventBus bus = getVertx().eventBus(); 36 | if (reply) { 37 | final AtomicReference responseRef = new AtomicReference(null); 38 | def replyHandler = new Handler>() { 39 | void handle(Message message) { 40 | synchronized (responseRef) { 41 | responseRef.set(message); 42 | responseRef.notifyAll(); 43 | } 44 | } 45 | } 46 | (format?:Format.STRING).send(bus, address, value, replyHandler); 47 | synchronized (responseRef) { 48 | if (responseRef.get() == null) { 49 | try { 50 | responseRef.wait(); 51 | } 52 | catch (InterruptedException cancelled) { 53 | } 54 | } 55 | } 56 | if (responseRef.get() != null) { 57 | Message response = responseRef.get(); 58 | out << response.body; 59 | } 60 | } else { 61 | (format?:Format.STRING).send(bus, address, value, null); 62 | } 63 | } 64 | 65 | @Usage("publish a JSON object as a message") 66 | @Command 67 | public void publish( 68 | @Usage("the address to send to") 69 | @Argument(name = "address") 70 | @Required String address, 71 | @Option(names = ["f","format"]) 72 | Format format, 73 | @Usage("the message") 74 | @Argument(name = "message", unquote = false) 75 | @Required List parts) { 76 | String value = join(parts); 77 | EventBus bus = getVertx().eventBus(); 78 | (format?:Format.STRING).publish(bus, address, value); 79 | } 80 | 81 | @Usage("read message from the bus") 82 | @Command 83 | public void subscribe( 84 | @Usage("the address to receive from") 85 | @Argument(name = "addresses") 86 | @Required List addresses) { 87 | 88 | // 89 | def bus = getVertx().eventBus() 90 | Map, String> handlers = [:] 91 | 92 | // 93 | try { 94 | 95 | // Create and register handlers 96 | addresses.each { address -> 97 | def handler = new Handler() { 98 | public void handle(Message message) { 99 | out.print(address); 100 | out.print(':'); 101 | out.println(message.body); 102 | out.flush(); 103 | } 104 | } 105 | handlers[handler] = address; 106 | bus.registerHandler(address, handler); 107 | } 108 | 109 | // Block until ctrl-c 110 | def o = new Object() 111 | synchronized (o) { 112 | o.wait(); 113 | } 114 | } 115 | catch (InterruptedException ignore) { 116 | // Done 117 | } 118 | finally { 119 | handlers.each { handler, address -> 120 | bus.unregisterHandler(address, handler); 121 | } 122 | } 123 | } 124 | } -------------------------------------------------------------------------------- /src/main/resources/crash/commands/vertx/module.groovy: -------------------------------------------------------------------------------- 1 | package crash.commands.vertx 2 | 3 | import org.crsh.cli.* 4 | import org.crsh.command.ScriptException 5 | import org.vertx.java.core.json.JsonObject 6 | import org.vertx.mods.VertxCommand 7 | 8 | @Usage("interact with vert.x modules") 9 | public class module extends VertxCommand { 10 | 11 | @Command 12 | @Usage("deploy a module") 13 | public void deploy( 14 | @Usage("the main to deploy") 15 | @Argument(name = "main") 16 | @Required String main, 17 | @Usage("the number of instances") 18 | @Option(names = ["i", "instances"]) 19 | Integer instances, 20 | @Usage("the module config") 21 | @Argument(name = "config", unquote = false) 22 | List parts) { 23 | JsonObject config = parts != null ? parseJson(parts) : null; 24 | try { 25 | if (instances == null) { 26 | if (config == null) { 27 | container.deployModule(main); 28 | } else { 29 | container.deployModule(main, config); 30 | } 31 | } else { 32 | if (config == null) { 33 | container.deployModule(main, instances); 34 | } else { 35 | container.deployModule(main, config, instances); 36 | } 37 | } 38 | } 39 | catch (Exception e) { 40 | throw new ScriptException("Could not deploy verticle $main: $e.message", e) 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/main/resources/crash/commands/vertx/sharedmap.groovy: -------------------------------------------------------------------------------- 1 | package crash.commands.vertx 2 | 3 | import org.crsh.cli.Argument 4 | import org.crsh.cli.Command 5 | import org.crsh.cli.Required 6 | import org.crsh.cli.Usage 7 | import org.vertx.java.core.shareddata.SharedData 8 | import org.vertx.mods.VertxCommand 9 | 10 | @Usage("interact with the vert.x shared map") 11 | public class sharedmap extends VertxCommand { 12 | 13 | @Usage("list content of a map") 14 | @Command 15 | public void keys( 16 | @Usage("The map") 17 | @Argument(name = "map") 18 | @Required String map) { 19 | SharedData shared = getVertx().sharedData(); 20 | shared.getMap(map).each { key, value -> 21 | context.provide([ 22 | key: key, 23 | value: value 24 | ]); 25 | } 26 | } 27 | 28 | @Usage("get a value") 29 | @Command 30 | public String get( 31 | @Usage("The map") 32 | @Argument(name = "map") 33 | @Required String map, 34 | @Usage("The key") 35 | @Argument(name = "key") 36 | @Required String key) { 37 | SharedData shared = getVertx().sharedData(); 38 | return shared.getMap(map).get(key); 39 | } 40 | 41 | @Usage("put a value") 42 | @Command 43 | public void put( 44 | @Usage("The map") 45 | @Argument(name = "map") 46 | @Required String map, 47 | @Usage("The key") 48 | @Argument(name = "key") 49 | @Required String key, 50 | @Usage("The value") 51 | @Argument(name = "value") 52 | @Required String value) { 53 | SharedData shared = getVertx().sharedData(); 54 | shared.getMap(map).put(key, value); 55 | } 56 | 57 | @Usage("remove a value") 58 | @Command 59 | public void rm( 60 | @Usage("The map") 61 | @Argument(name = "map") 62 | @Required String map, 63 | @Usage("The key") 64 | @Argument(name = "key") 65 | @Required String key) { 66 | SharedData shared = getVertx().sharedData(); 67 | shared.getMap(map).remove(key); 68 | } 69 | 70 | @Usage("clear a map") 71 | @Command 72 | public void clear( 73 | @Usage("The map") 74 | @Argument(name = "map") 75 | @Required String map) { 76 | SharedData shared = getVertx().sharedData(); 77 | shared.getMap(map).clear(); 78 | } 79 | 80 | @Usage("destroy a shared map") 81 | @Command 82 | public void destroy( 83 | @Usage("The map") 84 | @Argument(name = "map") 85 | @Required String map) { 86 | SharedData shared = getVertx().sharedData(); 87 | shared.removeMap(map); 88 | } 89 | } -------------------------------------------------------------------------------- /src/main/resources/crash/commands/vertx/verticle.groovy: -------------------------------------------------------------------------------- 1 | package crash.commands.vertx 2 | 3 | import org.crsh.cli.Argument 4 | import org.crsh.cli.Command 5 | import org.crsh.cli.Option 6 | import org.crsh.cli.Required 7 | import org.crsh.cli.Usage 8 | import org.crsh.command.ScriptException 9 | import org.vertx.java.core.json.JsonObject 10 | import org.vertx.mods.VertxCommand 11 | 12 | @Usage("interact with vert.x verticles") 13 | public class verticle extends VertxCommand { 14 | 15 | @Command 16 | @Usage("deploy a verticle") 17 | public void deploy( 18 | @Usage("The main to deploy") 19 | @Argument(name = "main") 20 | @Required String main, 21 | @Usage("The number of instances") 22 | @Option(names = ["i", "instances"]) 23 | Integer instances, 24 | @Usage("Specify the verticle to be a worker") 25 | @Option(names = ["w", "worker"]) 26 | boolean worker, 27 | @Usage("The verticle config") 28 | @Argument(name = "config", unquote = false) 29 | List parts) { 30 | JsonObject config = parts != null ? parseJson(parts) : null; 31 | try { 32 | if (worker) { 33 | if (instances == null) { 34 | if (config == null) { 35 | container.deployWorkerVerticle(main); 36 | } else { 37 | container.deployWorkerVerticle(main, config); 38 | } 39 | } else { 40 | if (config == null) { 41 | container.deployWorkerVerticle(main, instances); 42 | } else { 43 | container.deployWorkerVerticle(main, config, instances); 44 | } 45 | } 46 | } else { 47 | if (instances == null) { 48 | if (config == null) { 49 | container.deployVerticle(main); 50 | } else { 51 | container.deployVerticle(main, config); 52 | } 53 | } else { 54 | if (config == null) { 55 | container.deployVerticle(main, instances); 56 | } else { 57 | container.deployVerticle(main, config, instances); 58 | } 59 | } 60 | } 61 | } 62 | catch (Exception e) { 63 | throw new ScriptException("Could not deploy verticle $main: $e.message", e) 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /src/main/resources/crash/commands/vertx/vertx.groovy: -------------------------------------------------------------------------------- 1 | package crash.commands.vertx 2 | 3 | import org.crsh.cli.Argument 4 | import org.crsh.cli.Command 5 | import org.crsh.cli.Man 6 | import org.crsh.cli.Named 7 | import org.crsh.cli.Option 8 | import org.crsh.cli.Required 9 | import org.crsh.cli.Usage 10 | import org.crsh.text.ui.BorderStyle 11 | import org.crsh.text.ui.LabelElement 12 | import org.crsh.text.ui.RowElement 13 | import org.crsh.text.ui.TableElement 14 | import org.crsh.text.ui.TreeElement; 15 | import org.vertx.java.core.http.impl.DefaultHttpServer 16 | import org.vertx.java.core.json.JsonArray 17 | import org.vertx.java.core.json.JsonObject 18 | import org.vertx.java.core.net.impl.DefaultNetServer 19 | import org.vertx.java.core.net.impl.ServerID 20 | import org.vertx.java.platform.impl.Deployment 21 | import org.vertx.mods.VertxCommand 22 | 23 | @Usage("interact with vert.x") 24 | public class vertx extends VertxCommand { 25 | 26 | @Command 27 | @Usage("list existing http servers") 28 | public void http() { 29 | Map servers = getVertx().sharedHttpServers(); 30 | servers.each { id, server -> 31 | context.provide([ 32 | ID: "$id", 33 | TCPKeepAlive: "${server.TCPKeepAlive?:'-'}", 34 | TCPNoDelay: "${server.TCPNoDelay?:'-'}", 35 | SSL: "${server.SSL?:'-'}", 36 | ReceiveBufferSize: "${server.receiveBufferSize?:'-'}", 37 | SendBufferSize: "${server.sendBufferSize?:'-'}", 38 | SoLinger: "${server.soLinger?:'-'}", 39 | AcceptBacklog: "${server.acceptBacklog?:'-'}", 40 | TrafficClass: "${server.trafficClass?:'-'}", 41 | KeyStorePath: "${server.keyStorePath?:'-'}", 42 | KeyStorePassword: "${server.keyStorePassword?:'-'}", 43 | TrustStorePath: "${server.trustStorePath?:'-'}", 44 | TrustStorePassword: "${server.trustStorePassword?:'-'}", 45 | ]); 46 | } 47 | } 48 | 49 | @Command 50 | @Usage("list existing net servers") 51 | public void net() { 52 | Map servers = getVertx().sharedNetServers(); 53 | servers.each { id, server -> 54 | context.provide([ 55 | ID: "$id", 56 | TCPKeepAlive: "${server.TCPKeepAlive?:'-'}", 57 | TCPNoDelay: "${server.TCPNoDelay?:'-'}", 58 | SSL: "${server.SSL?:'-'}", 59 | ReceiveBufferSize: "${server.receiveBufferSize?:'-'}", 60 | SendBufferSize: "${server.sendBufferSize?:'-'}", 61 | SoLinger: "${server.soLinger?:'-'}", 62 | AcceptBacklog: "${server.acceptBacklog?:'-'}", 63 | TrafficClass: "${server.trafficClass?:'-'}", 64 | KeyStorePath: "${server.keyStorePath?:'-'}", 65 | KeyStorePassword: "${server.keyStorePassword?:'-'}", 66 | TrustStorePath: "${server.trustStorePath?:'-'}", 67 | TrustStorePassword: "${server.trustStorePassword?:'-'}", 68 | ReuseAddress: "${server.reuseAddress?:'-'}", 69 | ]); 70 | } 71 | } 72 | 73 | @Command 74 | @Usage("list existing deployments") 75 | public TreeElement deployments() { 76 | 77 | // 78 | Map nodes = [:]; 79 | TreeElement root = new TreeElement() 80 | def deployments = new HashMap(deployments); 81 | 82 | // Build the tree 83 | while (deployments.size() > 0) { 84 | def i = deployments.entrySet().iterator() 85 | def entry = i.next(); 86 | def name = entry.getKey(); 87 | def deployment = entry.getValue(); 88 | i.remove(); 89 | 90 | // 91 | def value = new TableElement(); 92 | value.setRightCellPadding(1); 93 | value.add(new RowElement().add(new LabelElement("name"), new LabelElement(deployment.name))); 94 | value.add(new RowElement().add(new LabelElement("id"), new LabelElement(deployment.modID))); 95 | 96 | // 97 | TreeElement node = nodes[name] = new TreeElement(value); 98 | 99 | // 100 | for (String childName : deployment.childDeployments) { 101 | def child = nodes[childName]; 102 | if (child != null) { 103 | node.addChild(child); 104 | } 105 | } 106 | 107 | // 108 | def parentName = deployment.parentDeploymentName; 109 | if (parentName != null) { 110 | def parent = nodes[parentName]; 111 | if (parent != null) { 112 | parent.addChild(node); 113 | } 114 | } else { 115 | root.addChild(node); 116 | } 117 | } 118 | 119 | // 120 | return root; 121 | } 122 | 123 | @Command 124 | @Usage("Provide more info about an existing deployment") 125 | public void deployment(@Argument @Required @Usage("the deployment name") String name) { 126 | def deployment = deployments[name]; 127 | if (deployment == null) { 128 | throw new ScriptException("Deployment $name does not exist"); 129 | } 130 | 131 | // 132 | context.provide(["property":"name", "value":deployment.name]); 133 | context.provide(["property":"parent", "value":deployment.parentDeploymentName]); 134 | context.provide(["property":"modID", "value":deployment.modID]); 135 | context.provide(["property":"instances", "value":deployment.instances]); 136 | context.provide(["property":"autoRedeploy", "value":deployment.autoRedeploy]); 137 | context.provide(["property":"classPath", "value":Arrays.asList(deployment.classpath)]); 138 | context.provide(["property":"childDeployments", "value":deployment.childDeployments]); 139 | context.provide(["property":"config", "value":deployment.config]); 140 | context.provide(["property":"main", "value":deployment.main]); 141 | } 142 | 143 | @Command 144 | @Usage("undeploy a deployment") 145 | public void undeploy( 146 | @Usage("The deployment id") 147 | @Argument(name = "id") 148 | @Required String id) { 149 | manager.undeploy(id, null); 150 | } 151 | 152 | @Command 153 | @Usage("display vert.x config") 154 | public void config() { 155 | JsonObject config = container.config(); 156 | if (config != null) { 157 | config.toMap().each { key, value -> 158 | context.provide([ 159 | name: key, 160 | value: value 161 | ]); 162 | } 163 | } 164 | } 165 | 166 | @Command 167 | @Named("execute") 168 | @Usage("execute a shell request") 169 | @Man("""\ 170 | Execute requests by publishing events to the "crash.execute" address. CRaSH has an event handler that execute 171 | requests: 172 | 173 | % vertx execute help 174 | 175 | Each request should be quoted or at least white spaces should be properly escaped: 176 | 177 | % vertx execute thread\\ ls "thread ls" 178 | 179 | When several requests are specified, they will execute sequentially: 180 | 181 | % vertx execute "repl groovy" "1+1" 182 | 183 | The optional reply-to argument can be used to receive the responses: 184 | 185 | % vertx execute --reply-to screen "thread ls" 186 | 187 | """) 188 | public void invoke( 189 | @Option(names = ["reply-to"]) @Usage("the optional reply to address for the response events") String replyTo, 190 | @Argument @Required @Usage("the requests to execute") List requests) { 191 | def bus = getVertx().eventBus() 192 | JsonArray requestsArray = new JsonArray(); 193 | requests.each { String request -> requestsArray.add(request); }; 194 | def event = new JsonObject().putArray("requests", requestsArray); 195 | if (replyTo != null) { 196 | event.putString("replyTo", replyTo); 197 | } 198 | bus.publish("crash.execute", event); 199 | } 200 | } -------------------------------------------------------------------------------- /src/main/resources/mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "main":"org.vertx.mods.CRaSHBusMod", 3 | "description": "This module provides a remote shell access for a Vert.x server", 4 | "licenses": ["GNU Lesser General Public License"], 5 | "author": "Julien Viet", 6 | "keywords": ["shell","admin","management"], 7 | "homepage": "https://github.com/crashub/mod-shell" 8 | } -------------------------------------------------------------------------------- /src/test/java/org/vertx/java/busmods/CRaSHBusMod.java: -------------------------------------------------------------------------------- 1 | package org.vertx.java.busmods; 2 | 3 | /** 4 | * Make {@link org.vertx.java.deploy.impl.java.JavaVerticleFactory} happy for testing purposes. 5 | * 6 | * @author Julien Viet 7 | */ 8 | public class CRaSHBusMod extends org.vertx.mods.CRaSHBusMod { 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/org/vertx/mods/VertxTestCase.java: -------------------------------------------------------------------------------- 1 | package org.vertx.mods; 2 | 3 | import org.junit.Test; 4 | import org.vertx.java.core.AsyncResult; 5 | import org.vertx.java.core.Handler; 6 | import org.vertx.java.core.eventbus.Message; 7 | import org.vertx.java.core.http.HttpServerRequest; 8 | import org.vertx.java.core.json.JsonObject; 9 | import org.vertx.java.core.net.NetSocket; 10 | import org.vertx.testtools.TestVerticle; 11 | 12 | import static org.vertx.testtools.VertxAssert.*; 13 | 14 | /** @author Julien Viet */ 15 | public class VertxTestCase extends TestVerticle { 16 | 17 | @Override 18 | public void start() { 19 | initialize(); 20 | container.deployModule( 21 | System.getProperty("vertx.modulename"), 22 | new JsonObject(). 23 | putString("crash.auth", "simple"). 24 | putString("crash.ssh.port", "2000"). 25 | putString("crash.auth.simple.username", "admin"). 26 | putString("crash.auth.simple.password", "admin"), 27 | new Handler>() { 28 | public void handle(AsyncResult stringAsyncResult) { 29 | startTests(); 30 | } 31 | }); 32 | } 33 | 34 | @Test 35 | public void testFoo() throws Exception { 36 | 37 | // testComplete(); 38 | 39 | // VerticleManager manager = vertx.getManager(); 40 | // manager.deployVerticle(); 41 | // System.out.println("vertx = " + getVertx()); 42 | // System.out.println("manager = " + manager); 43 | 44 | getVertx().createHttpServer().requestHandler(new Handler() { 45 | public void handle(HttpServerRequest event) { 46 | } 47 | }).setAcceptBacklog(50).listen(8080); 48 | 49 | getVertx().createNetServer().connectHandler(new Handler() { 50 | public void handle(NetSocket event) { 51 | } 52 | }).listen((8081)); 53 | 54 | 55 | vertx.eventBus().registerHandler("FOO", new Handler>() { 56 | public void handle(Message event) { 57 | System.out.println("Got message " + event); 58 | event.reply("Got your message"); 59 | } 60 | }); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/test/mods/org.crashub.shell-v1.0/mod.json: -------------------------------------------------------------------------------- 1 | {"main":"org.vertx.java.busmods.CRaSHBusMod"} -------------------------------------------------------------------------------- /src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=${log4j.level}, file, stdout 2 | 3 | # Direct log messages to stdout 4 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 5 | log4j.appender.stdout.Target=System.out 6 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 7 | log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n 8 | log4j.appender.stdout.threshold=ERROR 9 | 10 | # Direct log messages to a log file 11 | log4j.appender.file=org.apache.log4j.FileAppender 12 | log4j.appender.file.File=target/test.log 13 | log4j.appender.file.layout=org.apache.log4j.PatternLayout 14 | log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n 15 | --------------------------------------------------------------------------------