Een reactieve web applicatie bouwen met Vert.x – deel 2

Voorwoord

Als het je gelukt is om met aflevering 1 (episode-1) mee te doen, dan zou je nu een werkende Vert.x applicatie moeten hebben.

/reactieve-web-applicatie-met-vert-x/

Nu gaan we onze applicatie zodanig uitbreiden dat het een CSV-bestand kan importeren en het op basis daarvan informatie opvraagt van een externe bron (een website) met historische data over het weer.

Er is een heleboel nieuwe functionaliteit toegevoegd omdat we in deze aflevering de hele backend van de applicatie gaan afronden. De code werd te veel om in één artikel in detail te beschrijven dus mijn advies is: check de code uit en probeer het zelf te starten. Lees dit artikel terwijl je de code reviewt.

Dit artikel gaat met name in op wat specifieke Vert.x zaken zodat je wellicht wat beter begrijpt wat er allemaal gebeurt.

De code is beschikbaar vanaf:

https://github.com/Piepers/site-analytics/tree/episode-2

Laten we verder gaan met bouwen

Als je de code hebt uitgecheckt zul je zien dat de pom.xml file wat is aangepast. Ik heb een update uitgevoerd naar Vert.x 3.5.4. Dit was niet echt nodig, maar omdat het verschilt met onze vorige aflevering hier toch even genoemd. Overigens is de laatste versie op het moment van het schrijven van dit artikel versie 3.6.2. Upgraden naar Java 11 of 12 is wellicht een actie voor een volgende aflevering.

Laten we ons voor nu focussen op de applicatie die we aan het bouwen zijn. Eerst gaan we even in op het domein.

Het domein

Het domein is aangepast maar is nog altijd relatief eenvoudig. Alle klassen zitten in  com.ocs.analytics.domain. Het volgende diagram toont de klassen met hier en daar wat specifieke eigenschappen voor die klassen:

 

 

We gaan historische weergegevens ophalen van een website. De gegevens hiervan zijn gemodelleerd in WeatherMeasurement. Deze klasse bevat data over het weer van 1 klokuur. Een SiteStatistic instantie, representeert 1 klokuur aan site statistieken. Deze gegevens gaan we met onze applicatie importeren als CSV-bestand en correleren aan de weergegevens.

Wat belangrijk is om Vert.x in staat te stellen deze objecten om te zetten van en naar een bericht op de event-bus is…:

  • Ze te annoteren met @DataObject
  • Een toJson() methode te implementeren (in ons domein doen we dit in een interface die wordt “geïmplementeerd” door de domein klassen)
  • Een constructor te implementeren die een JsonObject omzet naar een instantie van die klasse.

Een voorbeeld van bovenstaande hebben we ook al in aflevering 1 gezien. De klassen bevatten daarnaast nog een aantal factory methods om CSV records om te zetten naar een instantie van die klasse en wat logica, validatie etc. wat niet Vert.x specifiek is. Zo hebben we voor SiteStatistics een addMeasurement() methode die een CSV record binnen krijgt. Op basis van deze data wordt een instantie van WeatherMeasurement toegevoegd aan een al bestaande SiteStatistic record.

Ik laat het verder aan jou over om de code verder te bekijken, mocht je dit interessant vinden.

Zoals je zult zien, zullen deze domein klassen het hart van de applicatie vormen omdat ze door de services en verticles worden gebruikt om berichten te converteren naar objecten, voor het aanroepen en uitvoeren van logica en om de gegevens te versturen via de event-bus.

Laten we naar de services en de verticles gaan kijken.

HttpServerVerticle

In aflevering 1 zijn we begonnen met het implementeren van de zogenaamde “HttpServerVerticle”. Zoals je wellicht nog weet is één van de belangrijkste onderdelen in Vert.x de aanwezigheid van deze Verticles die luisteren naar berichten op de event bus en deze berichten verwerken. Een verticle is een klasse die erft van AbstractVerticle. Je kunt vervolgens een aantal methoden overerven om gedrag toe te voegen.

In aflevering 1 begonnen we met de implementatie van de start() methode maar daar deed deze nog niet veel: we schreven enkel een logregel weg en gaven aan dat we klaar waren met verwerken met de aanroep: future.complete(). In deze aflevering gaan we wat meer toevoegen aan de http-server zodat deze http-requests kan afhandelen.

Om op een makkelijke manier berichten te verwerken en paden te definiëren gebruiken we de vertx-web module en declareren een Router instantie. Deze router heeft zogenaamde handlers die we implementeren in de Verticle. Bepaalde request-typen kunnen ook worden afgehandeld door reeds door Vert.x aangeboden functionaliteit. In het kort:

  1. Om statische content (zoals html pagina’s, Javascript bestanden, plaatjes etc.) te ontsluiten hebben we de StaticHandler
  2. Om requests af te handelen met een body (zoals POST requests) gebruiken we de BodyHandler
  3. Voor specifieke REST end-points implementeren we onze eigen gedrag met behulp van de Vert.x API.
  4. We definiëren onze end-points en koppelen deze aan de router.
  5. We starten de http-server en geven de router mee die de end-points vervolgens afhandelt.

Let op dat de volgorde waarin je de handlers definieert belangrijk is. De eerste match voor een bepaalde url wordt aangeroepen.

Als we dan naar de start() methode van de HttpServerVerticle kijken zien we:

Router router = Router.router(vertx);

router.route().handler(BodyHandler.create().setUploadsDirectory(".vertx/file-uploads"));
StaticHandler staticHandler = StaticHandler.create(); (…) router.route("/static/*").handler(staticHandler); router.route("/").handler(this::indexHandler);
router.post("/import").handler(this::importHandler);
Router subRouter = Router.router(vertx); router.mountSubRouter("/api", subRouter);

We koppelen eerst een BodyHandler aan de router. Interessante hieraan is dat Vert.x zelf, aan de hand van de headers van het request en de inhoud van het request, kan zien dat het eventueel om een bestand gaat. We configureren hier dat de bestanden moeten worden opgeslagen in een bepaalde directory. We kunnen het bestand daarna verwerken.

Verder hebben we een StaticHandler achter /static/* zitten (dus ieder request achter /static/ wordt hier afgehandeld). Deze handelt statische content af. Als er een request voor ‘/’ wordt ontvangen wordt de index handler aangeroepen. Deze methode implementeren we zelf in deze Verticle (in de indexHandler() methode).

We hebben ook een sub-router gedeclareerd. Hier kunnen we dan eenvoudig meer end-points toevoegen achter een bepaald url-deel, in dit geval /api. In deze aflevering doen we nog niets met deze end-point.

Nadat we de routes hebben gespecificeerd, gebruiken we dit in onze http-server en starten we hem:

this.vertx
        .createHttpServer()
        .requestHandler(router::accept)
        .rxListen(this.port)
        .subscribe(result -> {
            LOGGER.debug("The server has been started on port {}", result.actualPort());
            future.complete();
        }, throwable -> {
            LOGGER.error("Something went wrong");
            future.fail(throwable);
        });

Ik denk dat deze code voor zich spreekt. De Vert.x instantie kan worden aangeroepen om een http-server aan te maken en te starten. Het luistert naar de poort die we opgeven en omdat we de ‘rx’ services gebruiken die Vert.x biedt via de vertx-rx-java2 module kunnen we ‘rxListen(…)’ aanroepen en een “subscribe” doen om te wachten op het resultaat. Als we niet gebruik maakten van de RxJava functionaliteit dan zouden we de ‘listen()’ methode moeten gebruiken en met een callback handler moeten werken zoals dit:

this.vertx
        .createHttpServer()
        .requestHandler(router::accept)
        .listen(this.port, ar-> {
            if(ar.succeeded()) {
                future.complete();
            } else {
                future.fail(ar.cause());
            }
        });

In dit geval was deze werkwijze niet heel verschillend van de RxJava manier maar voor het maken van meerdere aanroepen achter elkaar is het veel leesbaarder om RxJava te gebruiken dan wanneer je allerlei geneste callback handlers zou moeten implementeren.

De handlers in de HttpServerVerticle klasse gebruiken ook RxJava. Omdat we het mixen met de RxJava services van de API maakt het een erg leesbaar geheel en makkelijk in het gebruik.

Als we de importHandler() methode als voorbeeld gebruiken: hier handelen we de file(-s) af die de gebruiker heeft geïmporteerd in de user-interface. Een deel daarvan wordt afgehandeld door de BodyHandler (zoals we eerder hebben gezien). Als je terug kijkt in de start() methode van de HttpServerVerticle zie je:

router.route().handler(BodyHandler.create().setUploadsDirectory(".vertx/file-uploads"));

We hadden eerder al gezien dat de BodyHandler de bestanden die de gebruiker upload zelf in een directory zet die we hebben gespecificeerd in de setUploadsDirectory() methode. Vervolgens kunnen we de afhandeling van dit bestand implementeren. Dit doen we in de importHandler() methode die wordt aangeroepen waneer we een POST request ontvangen naar de /import url. We hadden dit gedefinieerd toen we de router end-points declareerden:

router.post("/import").handler(this::importHandler);

De method leest nu het bestand uit de import directory en converteert deze naar een instantie van de FileUpload klasse (die ik heb aangemaakt om de inhoud van het bestand in een object te zetten). We geven dit object vervolgens door naar de event-bus met het volgende stukje code:

vertx
        .eventBus()
        .<JsonObject>rxSend("file-upload", jsonObject)
        .subscribe(message -> LOGGER.debug("Imported response: {}",
                        message
                        .body()
                        .encode()),
                throwable -> LOGGER.error("Something went wrong while importing the file.", throwable));

Hier roepen we de rxSend methode aan om de inhoud van het bestand (wat in jsonObject zit) naar een adres genaamd “file-upload” te versturen. We doen ook een “subscribe” zodat we worden genotificeerd wanneer de ontvanger van het bericht klaar is met afhandelen.

Merk op dat we dus twee verschillende manieren hebben om berichten te publiceren op de event-bus: een “publish” en een “send”. Bij een “publish” (het publiceren van een bericht) zet je een bericht op de event-bus voor potentieel meerdere ontvangers. Het is een soort “fire-and-forget” methode: je zet het bericht op de event bus maar bij het verwerken van het bericht krijg je geen signaal terug. Bij een “send” gaat het om point-to-point communicatie. In het geval van een “send” is het dus mogelijk om iets te doen met het bericht dat je terugverwacht van de ontvanger.

In bovenstaand voorbeeld loggen we het resultaat van het bericht. Als er een fout wordt geretourneerd, loggen we een fout. Het is waarschijnlijk overbodig te melden dat het hier om asynchrone communicatie gaat.

ImportProcessVerticle

 

Zoals je inmiddels weet zijn Verticles één van de belangrijkste componenten in Vert.x. Ze handelen de berichten af die ze via de event-bus binnen krijgen. Met de manier waarop dit gebeurt implementeert Vert.x het multi reactor design-pattern.

In de vorige paragraaf zagen we dat de HttpServerVerticle berichten naar een adres stuurde genaamd “file-upload”. Als we naar de ImportProcessVerticle kijken, zien we dat de start() methode een zogenaamde consumer gedefinieerd heeft voor hetzelfde adres. Dit is dus waar de berichten vanuit de HttpServerVerticle zullen worden opgepikt en worden afgehandeld.

De start() methode implementeert de handler voor de inhoud van het bericht. Het doet dat door het bericht eerst om te zetten naar een instantie van FileUpload en verkrijgt daarmee ook de naam van het bestand. Het gebruikt vervolgens een functie uit de Vert.x API om de inhoud te lezen en het bestand regel-voor-regel af te handelen:

vertx
        .fileSystem()
        .rxOpen(fileUpload.getUploadedFileName(), new OpenOptions())
        .flatMapObservable(csvFile -> 
                RecordParser
                .newDelimited("\n", csvFile)
                .toObservable()
                .map(buffer -> buffer.toString())
                // Expect a delimited string with 5 columns and specific values
                .filter(string -> string.trim().matches(IMPORT_PATTERN))
                .doFinally(csvFile::close)
        )
        .map(string -> SiteStatistic.from(string))
        .map(siteStatistic -> siteStatistics.addStatistic(siteStatistic))
        // Clean up when we're finished.
        .doFinally(() -> this.cleanup(fileUpload.getUploadedFileName()))
        .doOnComplete(() -> LOGGER.debug("Successfully processed the file, 
added {} items to the site statistics.", siteStatistics.getStatistics().size()))         .doOnComplete(() -> this.enrichStatistics(siteStatistics, message))         .doOnError(throwable -> message.fail(1, "Something went wrong " + throwable.getMessage()))         .subscribe();

De twee belangrijkste onderdelen uit de Vert.x API die hier worden gebruikt zijn de FileSystem klasse en de RecordParser. Zij werken samen om het bestand te lezen, een Observable aan te maken die de inhoud van het bestand ontsluit (regel-voor-regel) en als je goed kijkt, zie je dat de stream van records wordt omgezet in een instantie van SiteStatistic met de factory methode: from() in die klasse (voel je vrij om te kijken in die methode om te zien hoe hij dat precies werkt). Het resultaat wordt toegevoegd aan de lijst van statistieken in de SiteStatistics (meervoud) wrapper klasse.

De subscribe() heeft hier geen implementatie omdat we er in dit geval voor kiezen om de implementatie in de .doOnComplete en de .doOnerror methoden onder te brengen. Als deze code klaar is met verwerken wordt in de .doFinally() het bestand opgeruimd. Deze werkwijze is vergelijkbaar met het finally{} blok in een try-catch clausule: het wordt altijd aangeroepen, ook al wordt er een exception opgegooid.

We hebben in de vorige paragraaf ook gezien dat indien een verticle een bericht verzend via de send() methode op de event-bus het normaal gesproken ook een reactie terug verwacht. Dit is zichtbaar in de .doOnError en .doOnComplete methode-aanroepen. Het antwoord met een foutmelding in de vorm van een aanroep naar message.fail(…). Een successvolle response wordt geïmplementeerd in de .doOnComplete() met een aanroep naar de enrichStatistics(…) methode.

De enrichStatistics(…) methode roept een service aan die doormiddel van code generatie is omgezet naar een RxJava variant (zie volgende paragraaf) waarna de message.reply(…) wordt aangeroepen wanneer het succesvol verliep en een message.fail(…) in het andere geval.

Dit was erg veel informatie maar het is wel goed om even te kijken naar wat er nu eigenlijk gebeurt in een dergelijke flow. Je kunt zo in vrij weinig code toch veel doen zonder dat het onduidelijk wordt. Neem even de tijd om de code te bekijken en één en ander in je op te nemen.

In de rest van de applicatie zie je soortgelijke flows: er wordt een bericht verstuurd, een bepaald component handelt het af en stuurt een antwoord naar de zender. Dit gebeurt allemaal asynchroon. Uiteraard is bovenstaande implementatie slechts een voorbeeld. Er zijn uiteraard andere manieren om hetzelfde resultaat te bereiken.

SiteStatisticService

In de ImportProcessVerticle zagen we dat er een methode werd aangeroepen die er zo uit ziet:

siteStatisticsService.rxEnrichAnalytics(…)

Wat gebeurt hier?

Een fijne functionaliteit van Vert.x is dat je een service kunt implementeren die automatisch wordt getransformeerd naar een Vert.x service zodat je deze op een RxJava manier kunt aanroepen. Deze service gedraagt zich “onder water” hetzelfde als een Verticle (het luistert naar een adres op de event-bus en handelt berichten af) maar in de applicatie kan hij als service worden aangeroepen. Het voordeel is dus dat je een service kunt bouwen zonder dat je de code hoeft te schrijven om berichten te verzenden, te consumeren en te transformeren. Deze code wordt gegenereerd door het framework met behulp van maven of gradle plugins.

Om dit te bewerkstelligen implementeer je een service op een bepaalde manier, registreert deze service in de context van Vert.x en implementeert de interface. Vert.x zal dan de code genereren zodat je deze services kunt aanroepen zoals hierboven genoemd. De componenten die de service willen gebruiken, gebruiken in werkelijkheid een proxy naar de instantie die je registreert bij Vert.x. Laten we kijken hoe dit werkt.

De SiteStatisticService is een interface en is geannoteerd met twee annotaties:

  • @ProxyGen
  • @VertxGen

Deze twee annotaties geven aan dat Vert.x de code moet genereren zoals hierboven genoemd. In de SiteStatisticsService interface zie je ook twee factory methods en een adres voor die service. De create(Vertx vertx) methode instantieert een implementatie van de interface en de createProxy(…) methode instantieert een gegenereerde klasse, namelijk de SiteStatisticsServiceVertxEBProxy(…) klasse. Hoe weten we nu dat deze proxyklasse bestaat? Wel, die wordt gegenereerd door een configuratie in maven. De maven-compiler-plugin in het pom.xml bestand in de site-analytics-backend module is geconfigureerd met:

 <annotationProcessor>
 io.vertx.codegen.CodeGenProcessor
 </annotationProcessor>

Bouwen we dit project nu met maven, dan zullen we zien dat in de directory waar de output van de build wordt neergezet (normaal gesproken de “target” directory in je project) er nu een “generated-sources” directory bestaat met al deze gegenereerde klassen. Daarbij staat ook de SiteStatisticsServiceVertxEBProxy klasse.

Als je services wilt toevoegen aan je applicatie, moet je dus enkel onthouden dat je service naam wordt aangevuld met VertxEBProxy en dan kun je de createProxy() methode implementeren.

In de implementatie van de service moet je ook rekening houden met een paar regels. Ten eerste mogen de methoden nooit een return waarde hebben. Ten tweede moet er altijd een parameter van het type Handler worden meegegeven waarin de generieke types van die parameter het type van het resultaat aanduiden. Als de service methode geen resultaat heeft, dan is de parameter van het type: Handler<AsyncResult<Void>>. Als er wel een resultaat wordt verwacht, dan wordt de Void generieke type vervangen voor het type van de return waarde. In ons geval willen we in de service antwoorden met een SiteStatistics object. Daarom declareren we als parameter: Handler<AsyncResult<SiteStatistic>>. Er zijn daarnaast beperkingen in de type van deze parameter. Het type in AsyncResult kan zijn:

  • Een klasse die is geannoteerd met @DataObject (en een methode implementeert met een JsonObject als parameter);
  • Een primitief type ;
  • Een aantal van de “collection” klassen.

De service methode kan ook aanvullende parameters hebben. Voor deze parameters gelden dezelfde restricties. Onze service heeft maar één methode:

void enrichAnalytics(SiteStatistics statistics, Handler<AsyncResult<SiteStatistics>> result) throws ServiceException;

De ServiceException is weer een klasse uit de Vert.x API die een code en een bericht kan bevatten.

Tenslotte moet de methode altijd de handler aanroepen om aan te geven wat het resultaat was.  Laten we de code erbij pakken om te zien hoe dit precies werkt.

Zoals we beschreven, moet de service worden geregistreerd wanneer we de applicatie starten. In de AnalyticsApplication klasse (in de com.ocs.analytics package) hebben we nu een zogenaamde “ServiceBinder” waarin we de service registreren bij Vert.x. Dit is in feite de enige plek waar de service wordt geïnstantieerd, de rest van de componenten gebruiken dus de proxy instanties, die hun aanroepen delegeren naar de daadwerkelijke implementatie. We registreren de service met het adres dat we hebben opgenomen in de interface:

new ServiceBinder(vertx.getDelegate())
        .setAddress(SiteStatisticsService.EVENT_BUS_ADDRESS)
        .register(SiteStatisticsService.class, SiteStatisticsService.create(vertx.getDelegate()));

Dikgedrukt ter verduidelijking de regel waar we een instantie van de service aanmaken. Onthoud dat wanneer we een service op deze manier implementeren, dat de ServiceBinder altijd de implementatie moet hebben van de klasse en niet de proxy. De ImportProcessVerticle heeft een aanroep naar createProxy(), hier zie je dus dat deze verticle praat met een proxy vande service (in de init() methode van die verticle):

Override
public void init(Vertx vertx, Context context) {
    (…)
    this.siteStatisticsService = SiteStatisticsService.createProxy(rxVertx);
}

De site statistics service is nu één van de ontvangers van berichten op de event-bus maar zoals je kunt zien in de ImportProcessVerticle implementatie, wordt de service aangeroepen alsof het een gewone service is. Wat je jezelf ook moet realiseren is dat de service de berichten sequentieel van de event-bus haalt en asynchroon afhandelt.

De SiteStatisticsServiceImpl klasse gebruikt nog een andere funtionaliteit van de vertx-web component: de WebClient. Met de WebClient kunnen we HTTP requests doen op een asynchrone (reactieve) manier. Natuurlijk is het aanroepen van bronnen op het web altijd een blokkerende actie dus als we niet hoeven te wachten op het resultaat van de betreffende site maar slechts worden genotificeerd wanneer de aanroep klaar is, is dit zeer bevorderlijk voor de performance.

In de constructor van de SiteStatisticsServiceImpl zie je dat we een webclient instantiëren door de aanroep van zijn factory methode en we geven een instantie van WebClientOptions mee. Hierin zetten we een aantal opties die worden gedeeld door de aanroepen van de WebClient. Ook maken we een connection-pool aan:

WebClient.create(rxVertx,
        new WebClientOptions().
                setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 
(KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36")                 .setMaxPoolSize(10)                 .setLogActivity(false)); }

We gebruiken deze WebClient vervolgens in de enrichAnalytics methode.

Het wordt teveel om alle code in detail te beschrijven in deze methode maar een aantal dingen zijn wel interessant om eruit te pikken. De WebClient aanroepen zijn wederom mooi als “stream” uitgelijnd. We geven aan dat we een POST HTTP request willen doen, zetten de ssl vlag, stoppen headers in het request en zenden de inhoud naar de url. Wanneer de website zijn response retourneerd handelen we deze inhoud af. We gebruiken hier wederom de RecordParser gebruiken die we al eerder gezien hebben, omdat in dit geval de response een body heeft waar ook tekstgegevens in staan:

webClient
.post(443, WEATHER_BASE_URL, WEATHER_REQUEST_PER_HOUR_URL)
.ssl(true)
.putHeader("Accept",
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8")
.putHeader("Accept-Encoding", "gzip, deflate, br")
.putHeader("Content-Type", "application/x-www-form-urlencoded")
.rxSendForm(form)
.doOnError(throwable -> LOGGER.error("Something went wrong while sending a form.", throwable))
.flatMapObservable(response -> this.processResponse(response))
.doOnComplete(() -> LOGGER.debug("Completed processing the response"))
.subscribe(string -> this.processOneRecord(statistics, string),
throwable -> result.handle(Future.failedFuture(throwable)),
() -> {
result.handle(Future.succeededFuture(statistics));
});

Twee van de aanroepen zijn dik gedrukt:

  • handle(Future.failedFuture(throwable))
  • handle(Future.succeededFuture(statistics))

Op deze manier geven we aan de meegegeven Handler aan wat het resultaat is van de aanroep. Wanneer er zich een fout voordoet gebruiken we (Future.failedFuture()). Wanneer de aanroep en verwerking succesvol is  verlopen geven we een succesbericht terug (Future.succeededFuture(…)).

De service gebruiken in een webpagina

In episode-1 noemde we de toevoeging van de vertx-web-templ-freemarker module voor het maken van een prototype van de front-end van de applicatie. We gaan ons niet echt verdiepen in deze module maar we laten hierin toch even zien hoe we de backend aanroepen vanuit de pagina.

In de main/resources directory in het project staan twee mappen genaamd “templates” en “webroot”. De inhoud van deze mappen worden meeverpakt met onze applicatie. De “templates” directory heeft drie hele eenvoudige bestanden:

  • ftl
  • ftl
  • ftl

Als je in de index.ftl kijkt zie je dat we een form hebben gedefinieerd met één veld en een knop om het formulier te verzenden naar de backend. De templating module stelt ons ook in staat om de Vert.x context te gebruiken. In HttpServerVerticle hebben we een variabele “importing” gedeclareerd en we checken deze variabele in de template:

<#assign importing=context.importing!false>
<#if
importing != true>

(…)

<#else>
  <p>Processing your file.</p>
</#if>
(…)

Als de applicatie een bestand aan het importeren is, tonen we een bericht in het scherm, anders tonen we het formulier.

Wanneer we de applicatie starten (zie episode-1 als je vergeten bent hoe dit moest) is de applicatie beschikbaar op poort 8080. Open een browser en navigeer naar:

http://localhost:8080

Je zou nu een pagina als deze moeten zien:

In de main/resources map van het project heb ik een voorbeeld bestand bijgevoegd genaamd: example-analytics.csv. Dit bestand bevat twee maanden aan voorbeeld uurdata van site bezoeken aan een fictieve web-site. Je kunt dit bestand gebruiken om de applicatie te testen. Kort gezegd zal de volgende flow starten wanneer je het bestand importeert:

  • De HttpServerVerticle ontvangt het bestand (met behulp van de BodyHandler) en stelt de ImportProcessVerticle in staat om het bestand verder te verwerken.
  • De ImportProcessVerticle verzameld de inhoud van het bestand en stopt het in een instanties van SiteStatistic.
  • De SiteStatisticsService controleert de gegevens en verkrijgt weer-informatie van een bepaald weer-station in Nederland voor de uren waar het statistieken van heeft ontvangen van de web-site.
  • Wanneer het klaar is met het verrijken van de bestandsinhoud, antwoord het naar de HttpServerVerticle om aan te geven dat hij klaar is.

Ga je gang en probeer het uit!

Zoals je kunt zien gebeurt er nu nog niets met het resultaat van de service. Je ziet echter wel de nodige log statements voorbijkomen die duidelijk maken wat er gebeurt. Het afhandelen van de inhoud zullen we in de volgende aflevering implementeren.

Meer weten?

Ook een mobiele applicatie bouwen of eens in gesprek gaan? Neem volkomen vrijblijvend contact met ons via +31 40 3041330 of info@opencirclesolutions.com. Je kunt ook het formulier onderaan de pagina gebruiken om je vraag te stellen. Wij nemen dan snel contact met je op.

Tutorials

Een reactieve web applicatie bouwen met Vert.x – deel 3

Een reactieve web applicatie bouwen met Vert.x – deel 3

Voorwoord In deze laatste aflevering gaan we de front-end en een aantal onderdelen van de back-end verder implementeren. De backend stelt ons inmiddels in staat om site statistieken te importeren en te verrijken met weerdata uit een weerstation ergens in Nederland (De...

Lees meer
Een reactieve web applicatie bouwen met Vert.x

Een reactieve web applicatie bouwen met Vert.x

Dit is het eerste artikel in een serie artikelen: Een reactieve web applicatie bouwen met Vert.x Voorwoord Vandaag gaan we beginnen aan een nieuwe applicatie om een interessante Java API genaamd “Vert.x” te demonstreren. Dit artikel is onderdeel van een serie van...

Lees meer

Nieuwsbrief

Meld je nu aan voor Open Circle Stories en krijg een verzameling artikelen, tips, nieuws en verdiepingen in je mailbox.

Pin It on Pinterest

Share This