Application Server
Add an embedded web application serverλ︎
The status monitor service runs on top of an application server which handles the infrastructure of messaging over https and other Internet protocols.
There are several libraries to provide this, ring and http-kit being the most common.
Add dependencies for application server and routingλ︎
Edit the deps.edn
file for the project.
Add the http-kit library library for the application server
Add the compojure library for routing of requests
:deps
{org.clojure/clojure {:mvn/version "1.10.1"}
http-kit {:mvn/version "2.3.0"}
compojure {:mvn/version "1.6.1"}}
Add code to start the application serverλ︎
Edit the src/practicalli/status_monitor_service.clj
file
Include the http-kit server namespace and the compojure core namespace as requires in the ns
definition.
(ns practicalli.status-monitor-service
(:gen-class)
(:require [org.httpkit.server :as app-server]
[compojure.core :refer [defroutes GET]]))
Add an atom to hold a reference to the running application server. When the server is not running, the atom contains nil
.
Update the -main
function to start the application server using http-kit run-server
, optionally setting the port number the server should run on.
(defn -main
"Start the application server and run the application"
[port]
(println "INFO: Starting server on port: " port)
(reset! app-server-instance
(app-server/run-server #'status-monitor {:port (Integer/parseInt port)})))
Define a default route and handlerλ︎
Using the compojure defroutes
function, define the default route and response when the status-monitor app received a request on the main URL, (eg. http://localhost:8888/)
The route returns a hash-map that is the form of a response map. http-kit server transforms all response maps into https responses that are sent back to the requesting web browser.
Stop and restart server from REPLλ︎
Add functions to stop and restart the server, so change to the application code can be loaded in without having to stop the Clojure REPL.
Use the value in the app-server-instance atom to determine if the app-server is already running. If so, then send the instance the :timeout
key with a value of time to shut itself down.
(defn stop-app-server
"Gracefully shutdown the server, waiting 100ms"
[]
(when-not (nil? @app-server-instance)
(@app-server-instance :timeout 100)
(reset! app-server-instance nil)
(println "INFO: Application server stopped")))
With a REPL running the project, the server is started calling (-main)
and stopped by calling (stop-app-server)
. A restart function is simply calling the stop and start functions.
(defn restart-app-server
"Convenience function to stop and start the application server"
[]
(stop-app-server)
(-main))
Component lifecycle service
This approach is the essence of component lifecycle services such as mount, component and integrant.
Use the mount library if you are starting with component lifecycle services or require a clean and simple approach. Try integrant to take a data centric approach to such a service.
REPL experiment sectionλ︎
To help use the code during development a comment body has been included with calls to start, stop and restart the application.