1. Información general
En este artículo, analizaremos Vert.x, cubriremos sus conceptos básicos y crearemos un servicio web RESTfull simple con él.
Comenzaremos cubriendo los conceptos básicos sobre el kit de herramientas, avanzaremos lentamente hacia un servidor HTTP y luego crearemos el servicio RESTfull.
2. Acerca de Vert.x
Vert.x es un kit de herramientas de desarrollo de software de código abierto, reactivo y políglota de los desarrolladores de Eclipse.
La programación reactiva es un paradigma de programación, asociado con flujos asincrónicos, que responden a cualquier cambio o evento.
De manera similar, Vert.x usa un bus de eventos para comunicarse con diferentes partes de la aplicación y pasa eventos de forma asíncrona a los controladores cuando están disponibles.
Lo llamamos políglota debido a su soporte para múltiples lenguajes JVM y no JVM como Java, Groovy, Ruby, Python y JavaScript.
3. Configuración
Para usar Vert.x necesitamos agregar la dependencia de Maven:
io.vertx vertx-core 3.4.1
La última versión de la dependencia se puede encontrar aquí.
3. Vertículos
Los verticles son fragmentos de código que ejecuta el motor Vert.x. El kit de herramientas nos proporciona muchas clases de vértices abstractas, que se pueden ampliar e implementar como queramos.
Al ser políglota, los vertículos se pueden escribir en cualquiera de los idiomas admitidos. Una aplicación normalmente estaría compuesta por varios vértices que se ejecutan en la misma instancia de Vert.x y se comunican entre sí mediante eventos a través del bus de eventos.
Para crear un vértice en JAVA, la clase debe implementar la interfaz io.vertx.core.Verticle , o cualquiera de sus subclases.
4. Bus de eventos
Es el sistema nervioso de cualquier aplicación de Vert.x.
Al ser reactivos, los vértices permanecen inactivos hasta que reciben un mensaje o evento. Los Verticles se comunican entre sí a través del bus de eventos. El mensaje puede ser cualquier cosa, desde una cadena hasta un objeto complejo.
El manejo de mensajes es idealmente asíncrono, los mensajes se ponen en cola en el bus de eventos y el control se devuelve al remitente. Más tarde se retira de la cola al vértice de escucha. La respuesta se envía utilizando los métodos Future y callback .
5. Aplicación Vert.x simple
Creemos una aplicación simple con un vértice y la implementemos usando una instancia de vertx . Para crear nuestro vértice, ampliaremos el
Para crear nuestro verticle, ampliaremos la clase io.vertx.core.AbstractVerticle y anularemos el método start () :
public class HelloVerticle extends AbstractVerticle { @Override public void start(Future future) { LOGGER.info("Welcome to Vertx"); } }
El método start () será invocado por la instancia de vertx cuando se despliegue el verticle. El método toma io.vertx.core.Future como parámetro, que se puede utilizar para descubrir el estado de una implementación asíncrona del vértice.
Ahora implementemos el vértice:
public static void main(String[] args) { Vertx vertx = Vertx.vertx(); vertx.deployVerticle(new HelloVerticle()); }
Del mismo modo, podemos anular el método stop () de la clase AbstractVerticle , que se invocará al cerrar el vértice:
@Override public void stop() { LOGGER.info("Shutting down application"); }
6. Servidor HTTP
Ahora activemos un servidor HTTP usando un vértice:
@Override public void start(Future future) { vertx.createHttpServer() .requestHandler(r -> r.response().end("Welcome to Vert.x Intro"); }) .listen(config().getInteger("http.port", 9090), result -> { if (result.succeeded()) { future.complete(); } else { future.fail(result.cause()); } }); }
Hemos anulado el método start () para crear un servidor HTTP y le hemos adjuntado un controlador de solicitudes. El método requestHandler () se llama cada vez que el servidor recibe una solicitud.
Finalmente, el servidor está vinculado a un puerto, y se pasa un controlador AsyncResult al método listen () ya sea que la conexión o el inicio del servidor se realice correctamente utilizando future.complete () o future.fail () en el caso de cualquier errores.
Tenga en cuenta que: el método config.getInteger () lee el valor de la configuración del puerto HTTP que se carga desde un archivo conf.json externo .
Probemos nuestro servidor:
@Test public void whenReceivedResponse_thenSuccess(TestContext testContext) { Async async = testContext.async(); vertx.createHttpClient() .getNow(port, "localhost", "/", response -> { response.handler(responseBody -> { testContext.assertTrue(responseBody.toString().contains("Hello")); async.complete(); }); }); }
Para la prueba, usemos vertx-unit junto con JUnit .:
io.vertx vertx-unit 3.4.1 test
Podemos obtener la última versión aquí.
El verticle se implementa y en una instancia de vertx en el método setup () de la prueba unitaria:
@Before public void setup(TestContext testContext) { vertx = Vertx.vertx(); vertx.deployVerticle(SimpleServerVerticle.class.getName(), testContext.asyncAssertSuccess()); }
De manera similar, la instancia de vertx se cierra en el método @AfterClass tearDown () :
@After public void tearDown(TestContext testContext) { vertx.close(testContext.asyncAssertSuccess()); }
Observe que el método setup () de @BeforeClass toma un argumento TestContext . Esto ayuda a controlar y probar el comportamiento asincrónico de la prueba. Por ejemplo, la implementación de verticle es asincrónica, por lo que básicamente no podemos probar nada a menos que se implemente correctamente.
We have a second parameter to the deployVerticle() method, testContext.asyncAssertSuccess(). This is used to know if the server is deployed correctly or any failures occurred. It waits for the future.complete() or future.fail() in the server verticle to be called. In the case of a failure, it fails the test.
7. RESTful WebService
We have created an HTTP server, lets now use that to host an RESTfull WebService. In order do so we will need another Vert.x module called vertx-web. This gives a lot of additional features for web development on top of vertx-core.
Let's add the dependency to our pom.xml:
io.vertx vertx-web 3.4.1
We can find the latest version here.
7.1. Router and Routes
Let's create a router for our WebService. This router will take a simple route of GET method, and handler method getArtilces():
Router router = Router.router(vertx); router.get("/api/baeldung/articles/article/:id") .handler(this::getArticles);
The getArticle() method is a simple method that returns new Article object:
private void getArticles(RoutingContext routingContext) { String articleId = routingContext.request() .getParam("id"); Article article = new Article(articleId, "This is an intro to vertx", "baeldung", "01-02-2017", 1578); routingContext.response() .putHeader("content-type", "application/json") .setStatusCode(200) .end(Json.encodePrettily(article)); }
A Router, when receives a request, looks for the matching route, and passes the request further. The routes having a handler method associated with it to do sumthing with the request.
In our case, the handler invokes the getArticle() method. It receives the routingContext object as an argument. Derives the path parameter id, and creates an Article object with it.
In the last part of the method, let's invoke the response() method on the routingContext object and put the headers, set the HTTP response code, and end the response using the JSON encoded article object.
7.2. Adding Router to Server
Now let's add the router, created in the previous section to the HTTP server:
vertx.createHttpServer() .requestHandler(router::accept) .listen(config().getInteger("http.port", 8080), result -> { if (result.succeeded()) { future.complete(); } else { future.fail(result.cause()); } });
Notice that we have added requestHandler(router::accept) to the server. This instructs the server, to invoke the accept() of the router object when any request is received.
Now let's test our WebService:
@Test public void givenId_whenReceivedArticle_thenSuccess(TestContext testContext) { Async async = testContext.async(); vertx.createHttpClient() .getNow(8080, "localhost", "/api/baeldung/articles/article/12345", response -> { response.handler(responseBody -> { testContext.assertTrue( responseBody.toString().contains("\"id\" : \"12345\"")); async.complete(); }); }); }
8. Packaging Vert.x Application
To package the application as a deployable Java Archive (.jar) let's use Maven Shade plugin and the configurations in the execution tag:
io.vertx.core.Starter com.baeldung.SimpleServerVerticle ${project.build.directory}/${project.artifactId}-${project.version}-app.jar
In the manifestEntries, Main-Verticle indicates the starting point of the application and the Main-Class is a Vert.x class which, creates the vertx instance and deploys the Main-Verticle.
9. Conclusion
En este artículo introductorio, discutimos el kit de herramientas Vert.x y sus conceptos fundamentales. Vio cómo crear un servidor HTTP, con Vert.x y también un RESTFull WebService y mostró cómo probarlos usando vertx-unit .
Finalmente empaquetó la aplicación como un jar ejecutable.
La implementación completa de los fragmentos de código está disponible en GitHub.