0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-10 00:40:30 -05:00
penpot/backend/vendor/vertx
Andrey Antukh f57ce57cb3 ♻️ Refactor on docker and build scripts.
- Migrate to from ubuntu to debian.
- Add new buildenv image.
- Remove production images building from this repo.
- Better comaptibility with other architectures (arm64).
- Improved config management.
2020-01-20 13:26:37 +01:00
..
src/vertx 💄 Minor cosmetic changes on vertx.core ns. 2020-01-14 13:13:35 +01:00
test
.gitignore ♻️ Refactor on docker and build scripts. 2020-01-20 13:26:37 +01:00
deps.edn
LICENSE
mvn-upload.sh
README.md
tools.clj

vertx-clojure

A lightweight clojure adapter for vertx toolkit.

  • STATUS: alpha, in design and prototyping phase.
  • AUDIENCE: this is not a vertx documentation, this readme intends to explain only the clojure api

Example code on resources/user.clj file.

Install

Using deps.edn:

vertx-clojure/vertx {:mvn/version "0.0.0-SNAPSHOT"}

Using Leiningen:

[vertx-clojure/vertx "0.0.0-SNAPSHOT"]

User Guide

Verticles

Verticles is the basic "unit of execution" in the vertx toolkit. The concept is very simular to actors with the exception that a verticle does not have a inbox, and verticles communicates with other verticles or with the rest of the world using eventbus.

For create a verticle, you will need to create first a system instance (a name that we give to the Vertx instance):

(require '[vertx.core :as vc])

(def system (vc/system))

Then, you can proceed to create a verticle. A verticle concist on three functions: on-start, on-stop and on-error (where the on-start is mandatory the rest optional).

Lets define a dummy verticle that just prints hello world on start callback:

(defn on-start
  [ctx]
  (println "Hello world"))

(def dummy-verticle
  (vc/verticle {:on-start on-start}))

The dummy-verticle is a verticle factory, nothing is running at this momment. For run the verticle we need to deploy it using the previously created system instance:

(vc/deploy! system dummy-verticle)

The deploy! return value is a CompletionStage so you can deref it like a regular Future or use funcool/promesa for chain more complex transformations. The return value implements AutoCloseable that will allow to undeploy the verticle.

The deploy! function also accepts an additional parameter for options, and at this momment it only accepts as single option:

  • :instances - number of instances to launch of the same verticle.

Event Bus

The eventbus is the central communication system for verticles. It has different patterns of communication. On this documentation we will cover the: publish/subscribe and request/reply.

Lets define a simple echo verticle:

(require '[vertx.eventbus :as ve])

(defn on-message
  [msg]
  (:body msg))

(defn on-start
  [ctx]
  (vc/consumer ctx "test.echo" on-message))

(def echo-verticle
  (vc/verticle {:on-start on-start}))

And then, lets deploy 4 instances of it:

(vc/deploy! system echo-verticle {:instances 4})

Now, depending on how you send the messages to the "test.echo" topic, the message will be send to a single instance of will be broadcasted to all verticle instances subscribed to it.

To send a message and expect a response we need to use the ve/request! function:

@(ve/request! system {:foo "bar"})
;; => #vertx.eventbus.Msg{:body {:foo "bar"}}

The return value of on-message callback will be used as a reply and it can be any plain value or a CompletionStage.

When you want to send a message but you don't need the return value, there is the ve/send! function. And finally, if you want to send a message to all instances subscribed to a topic, you will need to use the ve/publish! function.

Http Server (vertx.http)

STATUS: pre-alpha: experimental & incomplete

This part will explain the low-level api for the http server. It is intended to be used as a building block for a more higher-level api or when you know that you exactly need for a performance sensitive applications.

The vertx.http exposes two main functions handler and server. Lets start creating a simple "hello world" http server:

(require '[vertx.http :as vh])

(defn hello-world-handler
  [req]
  {:status 200
   :body "Hello world\n"})

(defn on-start
  [ctx]
  (vh/server {:handler (vh/handler hello-world-handler)
              :port 2021}))

(->> (vc/verticle {:on-start on-start})
     (vc/deploy! system))

NOTE: you can start the server without creating a verticle but you will loss the advantage of scaling (using verticle instances parameter).

The req object is a plain map with the following keys:

  • :method the HTTP method.
  • :path the PATH of the requested URI.
  • :headers a map with string lower-cased keys of headers.
  • :vertx.http/request the underlying vertx request instance.
  • :vertx.http/response the underlying vertx response instance.

And the response object to the ring response, it can contain :status, :body and :headers.

WARNING: at this moment there are no way to obtain directly the body of request using clojure api, this is in design phase and we need to think how to expose it correctly without constraint too much the user code (if you have suggestions, please open an issue).

NOTE: If you want completly bypass the clojure api, pass a vertx Handler instance to server instead of using vertx.http/handler. There is the vertx.util/fn->handler helper that converts a plain clojure function into raw Handler instance.

Web Server (vertx.web)

STATUS: alpha

This part will explain the higher-level http/web server api. It is a general purpose with more clojure friendly api. It uses reitit-corefor the routing and sieppari for interceptors.

Lets start with a complete example:

(require '[vertx.http :as vh])
(require '[vertx.web :as vw])
(require '[vertx.web.interceptors :as vwi])

(defn hello-world-handler
  [req]
  {:status 200
   :body "Hello world!\n"})

(defn on-start
  [ctx]
  (let [routes [["/" {:interceptors [(vwi/cookies)]
                      :all hello-world-handler}]]
        handler (vw/handler ctx
                            (vw/assets "/static/*" {:root "resources/public/static"})
                            (vw/router routes))]
    (vh/server {:handler handler
                :port 2022})))

(->> (vc/verticle {:on-start on-start})
     (vc/deploy! system))

The routes are defined using reitit-core and the interceptors are using sieppari as underlying implementation. The request object is very similar to the one explained in vertx.http.

The main difference with vertx.http is that the handler is called when the body is ready to be used and is available under :body keyword on the request.

All additional features such that reading the query/form params, parse/write cookies, cors and file uploads are provided with interceptors as pluggable pieces:

  • vertx.web.interceptors/uploads parses the vertx uploaded file data structure and expose it as clojure maps under :uploads key.
  • vertx.web.interceptors/params parses the query string and form params in the body if the content-type is appropriate and exposes them under :params.
  • vertx.web.interceptors/cors properly sets the CORS headers.
  • vertx.web.interceptors/cookies handles the cookies reading from the request and cookies writing from the response.

License

This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.