├── .gitignore ├── build.gradle ├── settings.gradle └── src └── main └── java └── com └── ltamrazov └── vertxstarterguide ├── Main.java ├── ServiceBinder.java ├── config ├── API.java ├── AppConfig.java └── Events.java ├── domain └── Greeting.java ├── exceptions └── CustomException.java ├── service ├── HelloWorker.java └── HelloWorld.java └── web ├── GlobalHandlers.java ├── HelloController.java └── ServerVerticle.java /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | .idea 3 | gradle 4 | gradlew 5 | gradlew.bat 6 | build 7 | .DS_Store 8 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | group 'com.ltamrazov' 2 | version '1.0-SNAPSHOT' 3 | 4 | apply plugin: 'java' 5 | 6 | sourceCompatibility = 1.8 7 | targetCompatibility = 1.8 8 | 9 | repositories { 10 | mavenCentral() 11 | } 12 | 13 | dependencies { 14 | compile 'io.vertx:vertx-core:3.3.3' 15 | compile 'io.vertx:vertx-web:3.3.3' 16 | compile 'com.englishtown.vertx:vertx-guice:2.3.1' 17 | 18 | testCompile 'junit:junit:4.11' 19 | } 20 | 21 | jar { 22 | // by default fat jar 23 | archiveName = 'vertx-starter-guide-fat.jar' 24 | from { configurations 25 | .compile 26 | .collect { it.isDirectory() ? it : zipTree(it) } } 27 | manifest { 28 | attributes 'Main-Class': 'com.ltamrazov.vertxstarterguide.Main' 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'vertx-starter-guide' 2 | 3 | -------------------------------------------------------------------------------- /src/main/java/com/ltamrazov/vertxstarterguide/Main.java: -------------------------------------------------------------------------------- 1 | package com.ltamrazov.vertxstarterguide; 2 | 3 | import com.google.inject.Inject; 4 | import com.ltamrazov.vertxstarterguide.config.AppConfig; 5 | import com.ltamrazov.vertxstarterguide.service.HelloWorker; 6 | import com.ltamrazov.vertxstarterguide.web.ServerVerticle; 7 | import io.vertx.core.*; 8 | import io.vertx.core.json.JsonObject; 9 | 10 | /** 11 | * Created by levontamrazov on 2017-01-28. 12 | * Main class will instantiate vertx and deploy our ServiceLauncher 13 | */ 14 | public class Main extends AbstractVerticle{ 15 | 16 | /** 17 | * Main method that instantiates Vertx and deploys the launcher verticle 18 | * @param args 19 | */ 20 | public static void main(String[] args){ 21 | Vertx vertx = Vertx.vertx(); 22 | deploy(vertx, ServiceLauncher.class, new DeploymentOptions()); 23 | } 24 | 25 | /** 26 | * Service Launcher verticle will deploy all our service verticles 27 | * including server and workers. 28 | */ 29 | private static class ServiceLauncher extends AbstractVerticle{ 30 | private AppConfig appConfig; 31 | 32 | /** 33 | * Constructor takes AppConfig that can contain different deployment 34 | * information. 35 | * @param appConfig - AppConfig instance 36 | */ 37 | @Inject 38 | public ServiceLauncher(AppConfig appConfig) { 39 | this.appConfig = appConfig; 40 | } 41 | 42 | /** 43 | * Start method uses CompositeFuture to deploy all required verticles 44 | * 45 | * @param done 46 | */ 47 | @Override 48 | public void start(Future done) { 49 | DeploymentOptions serverOpts = new DeploymentOptions() 50 | .setWorkerPoolSize(appConfig.getServerThreads()); 51 | 52 | DeploymentOptions workerOpts = new DeploymentOptions() 53 | .setWorker(true) 54 | .setWorkerPoolSize(appConfig.getWorkerThreads()); 55 | 56 | CompositeFuture.all( 57 | deploy(vertx, ServerVerticle.class, serverOpts), 58 | deploy(vertx, HelloWorker.class, workerOpts) 59 | ).setHandler(r -> { 60 | if (r.succeeded()) { 61 | done.complete(); 62 | } else { 63 | done.fail(r.cause()); 64 | } 65 | }); 66 | } 67 | } 68 | 69 | /** 70 | * Deploy a vertx-guice verticle on a vertx instance with deployment options 71 | * @param vertx - Vertx instance to deploy on 72 | * @param verticle - Verticle class to deploy 73 | * @param opts - Deployment options to use for deployment 74 | * @return - Future that can be used to handle successful / failed deployments 75 | */ 76 | private static Future deploy(Vertx vertx, Class verticle, DeploymentOptions opts){ 77 | Future done = Future.future(); 78 | String deploymentName = "java-guice:" + verticle.getName(); 79 | JsonObject config = new JsonObject() 80 | .put("guice_binder", ServiceBinder.class.getName()); 81 | 82 | opts.setConfig(config); 83 | 84 | vertx.deployVerticle(deploymentName, opts, r -> { 85 | if(r.succeeded()){ 86 | System.out.println("Successfully deployed verticle: " + deploymentName); 87 | done.complete(); 88 | } 89 | else { 90 | System.out.println("Failed to deploy verticle: " + deploymentName); 91 | done.fail(r.cause()); 92 | } 93 | }); 94 | 95 | return done; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/com/ltamrazov/vertxstarterguide/ServiceBinder.java: -------------------------------------------------------------------------------- 1 | package com.ltamrazov.vertxstarterguide; 2 | 3 | import com.google.inject.AbstractModule; 4 | import com.google.inject.Provides; 5 | import com.google.inject.Singleton; 6 | import com.ltamrazov.vertxstarterguide.config.AppConfig; 7 | import com.ltamrazov.vertxstarterguide.service.HelloWorld; 8 | import com.ltamrazov.vertxstarterguide.web.HelloController; 9 | import io.vertx.core.Vertx; 10 | 11 | /** 12 | * Created by levontamrazov on 2017-02-02. 13 | */ 14 | public class ServiceBinder extends AbstractModule{ 15 | 16 | @Provides @Singleton 17 | public HelloController provideController(Vertx vertx, HelloWorld service){ 18 | return new HelloController(vertx, service); 19 | } 20 | 21 | @Provides @Singleton 22 | public HelloWorld provideService(){ 23 | return new HelloWorld(); 24 | } 25 | 26 | @Provides @Singleton 27 | public AppConfig provideAppConfig(){ 28 | return new AppConfig(30, 30); 29 | } 30 | 31 | @Override 32 | protected void configure() { 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/ltamrazov/vertxstarterguide/config/API.java: -------------------------------------------------------------------------------- 1 | package com.ltamrazov.vertxstarterguide.config; 2 | 3 | /** 4 | * Created by levontamrazov on 2017-01-28. 5 | */ 6 | public class API { 7 | public static final String LB_CHECK = "/lb_healthcheck"; 8 | public static final String GREETING = "/greet"; 9 | public static final String GREETING_W = "/greetw"; 10 | public static final String HELLO_API = "/hello"; 11 | public static final String ROOT = "/"; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/ltamrazov/vertxstarterguide/config/AppConfig.java: -------------------------------------------------------------------------------- 1 | package com.ltamrazov.vertxstarterguide.config; 2 | 3 | /** 4 | * Created by levontamrazov on 2017-02-02. 5 | */ 6 | public class AppConfig { 7 | private int serverThreads; 8 | private int workerThreads; 9 | 10 | public AppConfig(int serverThreads, int workerThreads) { 11 | this.serverThreads = serverThreads; 12 | this.workerThreads = workerThreads; 13 | } 14 | 15 | public int getServerThreads() { 16 | return serverThreads; 17 | } 18 | 19 | public int getWorkerThreads() { 20 | return workerThreads; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/ltamrazov/vertxstarterguide/config/Events.java: -------------------------------------------------------------------------------- 1 | package com.ltamrazov.vertxstarterguide.config; 2 | 3 | /** 4 | * Created by levontamrazov on 2017-02-02. 5 | */ 6 | public class Events { 7 | private Events() { 8 | } 9 | 10 | public static final String GREET = "EVENT_GREET"; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/ltamrazov/vertxstarterguide/domain/Greeting.java: -------------------------------------------------------------------------------- 1 | package com.ltamrazov.vertxstarterguide.domain; 2 | 3 | /** 4 | * Created by levontamrazov on 2017-01-28. 5 | */ 6 | public class Greeting { 7 | private String greeting; 8 | 9 | public Greeting() { 10 | } 11 | 12 | public Greeting(String name){ 13 | this.greeting = "Hello " + name; 14 | } 15 | 16 | public String getGreeting() { 17 | return greeting; 18 | } 19 | 20 | public void setGreeting(String greeting) { 21 | this.greeting = greeting; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/ltamrazov/vertxstarterguide/exceptions/CustomException.java: -------------------------------------------------------------------------------- 1 | package com.ltamrazov.vertxstarterguide.exceptions; 2 | 3 | /** 4 | * Created by levontamrazov on 2017-01-28. 5 | */ 6 | public class CustomException extends RuntimeException{ 7 | public CustomException(String msg, int status){ 8 | super(msg); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/ltamrazov/vertxstarterguide/service/HelloWorker.java: -------------------------------------------------------------------------------- 1 | package com.ltamrazov.vertxstarterguide.service; 2 | 3 | import com.google.inject.Inject; 4 | import com.ltamrazov.vertxstarterguide.config.Events; 5 | import com.ltamrazov.vertxstarterguide.domain.Greeting; 6 | import io.netty.handler.codec.http.HttpResponseStatus; 7 | import io.vertx.core.AbstractVerticle; 8 | import io.vertx.core.Future; 9 | import io.vertx.core.eventbus.MessageConsumer; 10 | import io.vertx.core.json.EncodeException; 11 | import io.vertx.core.json.Json; 12 | import io.vertx.core.json.JsonObject; 13 | 14 | /** 15 | * Created by levontamrazov on 2017-02-02. 16 | * Worker verticle that will process requests through the event 17 | * bus 18 | */ 19 | public class HelloWorker extends AbstractVerticle{ 20 | private HelloWorld service; 21 | 22 | /** 23 | * Constructor takes a service this verticle should be using. 24 | * @param service - HelloWorld service instance 25 | */ 26 | @Inject 27 | public HelloWorker(HelloWorld service) { 28 | this.service = service; 29 | } 30 | 31 | /** 32 | * Start method gets called when the verticle is deployed. 33 | * 34 | * @param done 35 | */ 36 | @Override 37 | public void start(Future done){ 38 | // Create a message consumer of type String 39 | // and set it to listen ont he GREET event. 40 | MessageConsumer consumer= vertx.eventBus().consumer(Events.GREET); 41 | 42 | // Handler for the event 43 | consumer.handler(m -> { 44 | // Parse the body into a json object, and call the service with 45 | // the required parameters 46 | JsonObject data = new JsonObject(m.body()); 47 | Greeting retval = service.greet(data.getString("name")); 48 | 49 | // reply to the sender or fail the message. 50 | try{ 51 | m.reply(Json.encode(retval)); 52 | }catch (EncodeException e){ 53 | m.fail(HttpResponseStatus.INTERNAL_SERVER_ERROR.code(), "Failed to encode data."); 54 | } 55 | }); 56 | 57 | done.complete(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/ltamrazov/vertxstarterguide/service/HelloWorld.java: -------------------------------------------------------------------------------- 1 | package com.ltamrazov.vertxstarterguide.service; 2 | 3 | import com.ltamrazov.vertxstarterguide.domain.Greeting; 4 | 5 | /** 6 | * Created by levontamrazov on 2017-01-28. 7 | * Sample service class. 8 | */ 9 | public class HelloWorld { 10 | public HelloWorld(){ 11 | // initiate your class 12 | } 13 | 14 | /** 15 | * Return a greeting for a particular name. 16 | * @param name - Name to greet 17 | * @return - Greeting object 18 | */ 19 | public Greeting greet(String name){ 20 | try{ 21 | Thread.sleep(1500); 22 | return new Greeting(name); 23 | }catch (InterruptedException e){ 24 | throw new RuntimeException("This is a safe message"); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/ltamrazov/vertxstarterguide/web/GlobalHandlers.java: -------------------------------------------------------------------------------- 1 | package com.ltamrazov.vertxstarterguide.web; 2 | 3 | import com.ltamrazov.vertxstarterguide.exceptions.CustomException; 4 | import io.netty.handler.codec.http.HttpResponseStatus; 5 | import io.vertx.core.Vertx; 6 | import io.vertx.core.json.JsonObject; 7 | import io.vertx.ext.web.Router; 8 | import io.vertx.ext.web.RoutingContext; 9 | 10 | /** 11 | * Created by levontamrazov on 2017-01-28. 12 | */ 13 | public class GlobalHandlers { 14 | private GlobalHandlers(){} 15 | 16 | public static void lbCheck(RoutingContext ctx){ 17 | ctx.response().end("ok"); 18 | } 19 | 20 | public static void error(RoutingContext ctx){ 21 | int status; 22 | String msg; 23 | 24 | // Get thrown exception from context 25 | Throwable failure = ctx.failure(); 26 | 27 | if(CustomException.class.isAssignableFrom(failure.getClass())){ 28 | msg = failure.getMessage(); 29 | status = HttpResponseStatus.BAD_REQUEST.code(); 30 | } 31 | else { 32 | System.out.println(failure); 33 | msg = "Sorry, something went wrong"; 34 | status = HttpResponseStatus.INTERNAL_SERVER_ERROR.code(); 35 | } 36 | 37 | // Log the error, and send a json encoded response. 38 | JsonObject res = new JsonObject().put("status", status).put("message", msg); 39 | ctx.response().setStatusCode(status).end(res.encode()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/ltamrazov/vertxstarterguide/web/HelloController.java: -------------------------------------------------------------------------------- 1 | package com.ltamrazov.vertxstarterguide.web; 2 | 3 | import com.google.inject.Inject; 4 | import com.ltamrazov.vertxstarterguide.config.API; 5 | import com.ltamrazov.vertxstarterguide.config.Events; 6 | import com.ltamrazov.vertxstarterguide.service.HelloWorld; 7 | import io.vertx.core.AsyncResult; 8 | import io.vertx.core.Vertx; 9 | import io.vertx.core.eventbus.Message; 10 | import io.vertx.core.json.EncodeException; 11 | import io.vertx.core.json.Json; 12 | import io.vertx.core.json.JsonObject; 13 | import io.vertx.ext.web.Router; 14 | import io.vertx.ext.web.RoutingContext; 15 | 16 | /** 17 | * Created by levontamrazov on 2017-01-28. 18 | * Hello controller is responsible fort he Hello service api. 19 | * It contains all the relevant handlers and creates a router 20 | * that maps the end points to the handlers. 21 | */ 22 | public class HelloController { 23 | private Vertx vertx; 24 | private HelloWorld service; 25 | private Router router; 26 | 27 | /** 28 | * Takes an instance of Vertx for blocking calls and 29 | * the service it should be using. 30 | * @param vertx - Vertx instance 31 | * @param service - HelloWorld service 32 | */ 33 | @Inject 34 | public HelloController(Vertx vertx, HelloWorld service){ 35 | this.vertx = vertx; 36 | this.service = service; 37 | } 38 | 39 | /** 40 | * Return a configured Router instance 41 | * @return - vertx Router 42 | */ 43 | public Router getRouter(){ 44 | if(router == null){ 45 | router = Router.router(vertx); 46 | router.get(API.GREETING).handler(this::getGreeting); 47 | router.get(API.GREETING_W).handler(this::getGreetingW); 48 | } 49 | 50 | return router; 51 | } 52 | 53 | /** 54 | * Handler that uses executeBlocking to process request 55 | * async 56 | * @param ctx - RoutingContext for the request 57 | */ 58 | private void getGreeting(RoutingContext ctx){ 59 | String name = ctx.request().getParam("name"); 60 | vertx.executeBlocking( 61 | fut -> { fut.complete(service.greet(name)); }, 62 | false, // IMPORTANT TO MAKE THIS FALSE 63 | res -> { 64 | handleAsyncResponse(res, ctx); } 65 | ); 66 | } 67 | 68 | /** 69 | * Handler that uses the event bus and a worker verticle 70 | * to process the request async 71 | * @param ctx - RoutingContext for the request 72 | */ 73 | private void getGreetingW(RoutingContext ctx){ 74 | String data = new JsonObject() 75 | .put("name", ctx.request().getParam("name")) 76 | .encode(); 77 | 78 | vertx.eventBus().send(Events.GREET, data, res -> { handleEventBusResponse(res, ctx); }); 79 | } 80 | 81 | /** 82 | * Helper method for handling executeBlocking responses 83 | * @param res - AsynResult from executeBlocking 84 | * @param ctx - RoutingContext for the request 85 | */ 86 | private void handleAsyncResponse(AsyncResult res, RoutingContext ctx){ 87 | // Handler for the future. If successful, encode result and send 88 | if(res.succeeded()){ 89 | try { 90 | ctx.response().end(Json.encode(res.result())); 91 | } 92 | catch(EncodeException e){ 93 | ctx.fail(new RuntimeException("Failed to encode results.")); 94 | } 95 | 96 | } 97 | else { 98 | ctx.fail(res.cause()); 99 | } 100 | } 101 | 102 | /** 103 | * Helper method for handling event bus responses 104 | * @param res - Event bus response 105 | * @param ctx - RoutingContext for the request 106 | */ 107 | private void handleEventBusResponse(AsyncResult> res, RoutingContext ctx){ 108 | if(res.succeeded()){ 109 | ctx.response().end(res.result().body().toString()); 110 | } 111 | else { 112 | ctx.fail(res.cause()); 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/com/ltamrazov/vertxstarterguide/web/ServerVerticle.java: -------------------------------------------------------------------------------- 1 | package com.ltamrazov.vertxstarterguide.web; 2 | 3 | import com.google.inject.Inject; 4 | import com.ltamrazov.vertxstarterguide.config.API; 5 | import com.ltamrazov.vertxstarterguide.service.HelloWorld; 6 | import io.vertx.core.AbstractVerticle; 7 | import io.vertx.core.Future; 8 | import io.vertx.core.http.HttpMethod; 9 | import io.vertx.ext.web.Router; 10 | import io.vertx.ext.web.handler.BodyHandler; 11 | import io.vertx.ext.web.handler.CorsHandler; 12 | 13 | import java.util.HashSet; 14 | import java.util.Set; 15 | 16 | /** 17 | * Created by levontamrazov on 2017-01-28. 18 | */ 19 | public class ServerVerticle extends AbstractVerticle{ 20 | private HelloController controller; 21 | 22 | @Inject 23 | public ServerVerticle(HelloController controller) { 24 | this.controller = controller; 25 | } 26 | 27 | @Override 28 | public void start(Future future) throws Exception{ 29 | int PORT = 8181; 30 | Router helloRouter = controller.getRouter(); 31 | 32 | Router mainRouter = Router.router(vertx); 33 | mainRouter.route().consumes("application/json"); 34 | mainRouter.route().produces("application/json"); 35 | 36 | Set allowHeaders = getAllowedHeaders(); 37 | Set allowMethods = getAllowedMethods(); 38 | mainRouter.route().handler(BodyHandler.create()); 39 | mainRouter.route().handler(CorsHandler.create("*") 40 | .allowedHeaders(allowHeaders) 41 | .allowedMethods(allowMethods)); 42 | 43 | mainRouter.mountSubRouter(API.HELLO_API, helloRouter); 44 | mainRouter.get(API.LB_CHECK).handler(GlobalHandlers::lbCheck); 45 | mainRouter.route().failureHandler(GlobalHandlers::error); 46 | 47 | // Create the http server and pass it the router 48 | vertx.createHttpServer() 49 | .requestHandler(mainRouter::accept) 50 | .listen(PORT, res -> { 51 | if(res.succeeded()){ 52 | System.out.println("Server listening on port " + PORT); 53 | future.complete(); 54 | } 55 | else{ 56 | System.out.println("Failed to launch server"); 57 | future.fail(res.cause()); 58 | } 59 | }); 60 | } 61 | 62 | private Set getAllowedHeaders(){ 63 | Set allowHeaders = new HashSet<>(); 64 | allowHeaders.add("x-requested-with"); 65 | allowHeaders.add("Access-Control-Allow-Origin"); 66 | allowHeaders.add("origin"); 67 | allowHeaders.add("Content-Type"); 68 | allowHeaders.add("accept"); 69 | return allowHeaders; 70 | } 71 | 72 | private Set getAllowedMethods(){ 73 | Set allowMethods = new HashSet<>(); 74 | allowMethods.add(HttpMethod.GET); 75 | allowMethods.add(HttpMethod.POST); 76 | allowMethods.add(HttpMethod.DELETE); 77 | allowMethods.add(HttpMethod.PATCH); 78 | return allowMethods; 79 | } 80 | } 81 | --------------------------------------------------------------------------------