Browse Source

Update to latest Pedestal version; Use Pedestal's fast setup (#2521)

Paul deGrandis 8 years ago
parent
commit
dce9eae096

+ 31 - 3
frameworks/Clojure/pedestal/.gitignore

@@ -1,5 +1,33 @@
+# Java related
+pom.xml
+pom.xml.asc
+*jar
+*.class
+
+# Leiningen
+classes/
+lib/
+native/
+checkouts/
 target/
 target/
-tmp/
-logs/
-.lein-repl-history
+.lein-*
+repl-port
 .nrepl-port
 .nrepl-port
+.repl
+
+# Temp Files
+*.orig
+*~
+.*.swp
+.*.swo
+*.tmp
+*.bak
+
+# OS X
+.DS_Store
+
+# Logging
+*.log
+/logs/
+
+

+ 6 - 0
frameworks/Clojure/pedestal/README.md

@@ -2,6 +2,11 @@
 
 
 This is the [Pedestal](https://github.com/pedestal/pedestal) portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
 This is the [Pedestal](https://github.com/pedestal/pedestal) portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
 
 
+## Quick testing/benching
+
+You can use the `run.sh` and `perfit.sh` scripts to get a rough idea how
+changes will impact a final benchmark.
+
 ## Test URLs
 ## Test URLs
 ### JSON Encoding Test
 ### JSON Encoding Test
 `http://localhost:8080/json`
 `http://localhost:8080/json`
@@ -20,3 +25,4 @@ This is the [Pedestal](https://github.com/pedestal/pedestal) portion of a [bench
 
 
 ### Plaintext
 ### Plaintext
 `http://localhost:8080/plaintext`
 `http://localhost:8080/plaintext`
+

+ 2 - 2
frameworks/Clojure/pedestal/benchmark_config.json

@@ -17,12 +17,12 @@
       "framework": "pedestal",
       "framework": "pedestal",
       "language": "Clojure",
       "language": "Clojure",
       "flavor": "None",
       "flavor": "None",
-      "platform": "Undertow",
+      "platform": "Jetty",
       "webserver": "None",
       "webserver": "None",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "pedestal",
       "display_name": "pedestal",
-      "notes": "undertow"
+      "notes": "jetty"
     }
     }
   }]
   }]
 }
 }

+ 9 - 16
frameworks/Clojure/pedestal/config/logback.xml

@@ -1,5 +1,8 @@
 <!-- Logback configuration. See http://logback.qos.ch/manual/index.html -->
 <!-- Logback configuration. See http://logback.qos.ch/manual/index.html -->
-<configuration scan="true" scanPeriod="10 seconds">
+<configuration scan="false">
+
+  <!-- Silence Logback's own status messages about config parsing -->
+  <statusListener class="ch.qos.logback.core.status.NopStatusListener" />
 
 
   <!-- Simple file output -->
   <!-- Simple file output -->
   <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
   <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
@@ -8,21 +11,17 @@
       <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
       <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
     </encoder>
     </encoder>
 
 
-    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
       <!-- rollover daily -->
       <!-- rollover daily -->
       <fileNamePattern>logs/pedestal-api-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
       <fileNamePattern>logs/pedestal-api-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
-      <timeBasedFileNamingAndTriggeringPolicy
-          class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
-        <!-- or whenever the file size reaches 64 MB -->
-        <maxFileSize>64 MB</maxFileSize>
-      </timeBasedFileNamingAndTriggeringPolicy>
+      <!-- or whenever the file size reaches 64 MB -->
+      <maxFileSize>64 MB</maxFileSize>
     </rollingPolicy>
     </rollingPolicy>
 
 
     <!-- Safely log to the same file from multiple JVMs. Degrades performance! -->
     <!-- Safely log to the same file from multiple JVMs. Degrades performance! -->
-    <prudent>true</prudent>
+    <prudent>false</prudent>
   </appender>
   </appender>
 
 
-
   <!-- Console output -->
   <!-- Console output -->
   <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
   <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
     <!-- encoder defaults to ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
     <!-- encoder defaults to ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
@@ -35,7 +34,6 @@
     </filter>
     </filter>
   </appender>
   </appender>
 
 
-
   <!-- Enable FILE and STDOUT appenders for all log messages.
   <!-- Enable FILE and STDOUT appenders for all log messages.
        By default, only log at level INFO and above. -->
        By default, only log at level INFO and above. -->
   <root level="INFO">
   <root level="INFO">
@@ -43,10 +41,5 @@
     <appender-ref ref="STDOUT" />
     <appender-ref ref="STDOUT" />
   </root>
   </root>
 
 
-  <!-- For loggers in the these namespaces, log at all levels. -->
-  <logger name="user" level="ALL" />
-  <!-- To log pedestal internals, enable this and change ThresholdFilter to DEBUG
-    <logger name="io.pedestal" level="ALL" />
-  -->
-
 </configuration>
 </configuration>
+

+ 29 - 0
frameworks/Clojure/pedestal/perfit.sh

@@ -0,0 +1,29 @@
+#!/bin/bash
+
+# To run this, just call: sh ./perfit.sh
+# You may also optionally pass a port and URL, it must start with a slashL sh ./perfit.sh 8383 /about
+
+# THIS IS TO ONLY GET A BASELINE MARK!  THIS DOES NOT REFLECT PROD PERFORMANCE
+# THE GOAL IS TO HAVE SO NOTION OF PERF AND POTENTIAL REGRESSIONS
+
+HOST='127.0.0.1'
+port=8080
+PORT=${1:-$port}
+url="/"
+URL=${2:-$url}
+WRK2="`which wrk2` -R 1000"
+WRK=`which wrk`
+
+## Uncomment the desired benchmarks
+#httperf --close-with-reset --session-cookies --hog  --server $HOST --port $PORT --uri $URL --wsess=40,5,2 --rate 2 --timeout 5
+#echo
+#siege -c 20 -t 30S http://$HOST:$PORT$URL 1> /dev/null
+#echo
+echo "Measuring constant throughput, consistent latency..."
+echo
+$WRK2 -t4 -c24 -d10s http://$HOST:$PORT$URL
+echo
+echo "Benching..."
+echo
+$WRK -t2 -c12 -d30s http://$HOST:$PORT$URL
+

+ 48 - 13
frameworks/Clojure/pedestal/project.clj

@@ -3,23 +3,58 @@
   :url "https://github.com/TechEmpower/FrameworkBenchmarks"
   :url "https://github.com/TechEmpower/FrameworkBenchmarks"
   :license {:name "Eclipse Public License"
   :license {:name "Eclipse Public License"
             :url "http://www.eclipse.org/legal/epl-v10.html"}
             :url "http://www.eclipse.org/legal/epl-v10.html"}
-  :dependencies [[org.clojure/clojure "1.8.0"]
-                 [io.pedestal/pedestal.service "0.5.0"]
-                 [io.pedestal/pedestal.jetty "0.5.0"]
-                 [ch.qos.logback/logback-classic "1.1.2" :exclusions [org.slf4j/slf4j-api]]
-                 [org.slf4j/jul-to-slf4j "1.7.7"]
-                 [org.slf4j/jcl-over-slf4j "1.7.7"]
-                 [org.slf4j/log4j-over-slf4j "1.7.7"]
-                 [org.clojure/data.json "0.2.6"]
+  :dependencies [[org.clojure/clojure "1.9.0-alpha14"]
+                 [io.pedestal/pedestal.service "0.5.2"]
+                 [io.pedestal/pedestal.jetty "0.5.2"]
                  [org.clojure/java.jdbc "0.4.2"]
                  [org.clojure/java.jdbc "0.4.2"]
                  [korma "0.4.2"]
                  [korma "0.4.2"]
                  [mysql/mysql-connector-java "5.1.38"]
                  [mysql/mysql-connector-java "5.1.38"]
+                 [com.zaxxer/HikariCP "2.5.1" :exclusions [[org.slf4j/slf4j-api]]]
                  [hiccup "1.0.5"]]
                  [hiccup "1.0.5"]]
   :min-lein-version "2.0.0"
   :min-lein-version "2.0.0"
-  :resource-paths ["config", "resources"]
-  :profiles {:dev {:aliases {"run-dev" ["trampoline" "run" "-m" "pedestal.server/run-dev"]}
-                   :dependencies [[io.pedestal/pedestal.service-tools "0.3.1"]]}}
+  :resource-paths ["config"]
+  :profiles {:uberjar {:global-vars ^:replace {*warn-on-reflection* true
+                                               *unchecked-math* :warn-on-boxed
+                                               ;*compiler-options* {:disable-locals-clearing true}
+                                               *assert* false}
+                       :jvm-opts ["-D\"clojure.compiler.direct-linking=true\""
+                                  "-D\"io.pedestal.log.overrideLogger=nil\""
+                                  "-D\"io.pedestal.log.defaultMetricsRecorder=nil\""]
+                       :aot [pedestal.pdg]
+                       :main pedestal.pdg}
+             :srepl {:jvm-opts ^:replace ["-d64" "-server"
+                                          "-XX:+UseG1GC"
+                                          "-D\"clojure.compiler.direct-linking=true\""
+                                          "-Dclojure.server.repl={:port 5555 :accept clojure.core.server/repl}"]}
+             :dev {:aliases {"crepl" ["trampoline" "run" "-m" "clojure.main/main"]
+                             "srepl" ["with-profile" "srepl" "trampoline" "run" "-m" "clojure.main/main"]
+                             "run-dev" ["trampoline" "run" "-m" "pedestal.pdg/run-dev"]}
+                   :dependencies [;[io.pedestal/pedestal.service-tools "0.5.2"]
+                                  [ch.qos.logback/logback-classic "1.1.3" :exclusions [org.slf4j/slf4j-api]]
+                                  [org.slf4j/jul-to-slf4j "1.7.22"]
+                                  [org.slf4j/jcl-over-slf4j "1.7.22"]
+                                  [org.slf4j/log4j-over-slf4j "1.7.22"]
+                                  [criterium "0.4.4"]]}}
   :auto-clean false
   :auto-clean false
-  :main pedestal.server
-  :aot [pedestal.server]
+  :jvm-opts ^:replace [;; Turn on Clojure's Direct Linking
+                       "-D\"clojure.compiler.direct-linking=true\""
+                       ;; Turn off Pedestal's Metrics
+                       "-D\"io.pedestal.defaultMetricsRecorder=nil\""
+                       ;"-d64" "-server"
+                       "-Xms1g"                             ;"-Xmx1g"
+                       ;"-XX:+UnlockCommercialFeatures"      ;"-XX:+FlightRecorder"
+                       ;"-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8030"
+                       "-XX:+UseG1GC"
+                       ;"-XX:+UseConcMarkSweepGC" "-XX:+UseParNewGC" "-XX:+CMSParallelRemarkEnabled"
+                       ;"-XX:+ExplicitGCInvokesConcurrent"
+                       "-XX:+AggressiveOpts"
+                       ;-XX:+UseLargePages
+                       "-XX:+UseCompressedOops"]
+  :global-vars {*warn-on-reflection* true
+                *unchecked-math* :warn-on-boxed
+                ;*compiler-options* {:disable-locals-clearing true}
+                *assert* true}
+  :pedantic? :abort
+  :main ^{:skip-aot true} pedestal.pdg
   :uberjar-name "pedestal-standalone.jar")
   :uberjar-name "pedestal-standalone.jar")
+

+ 4 - 0
frameworks/Clojure/pedestal/run.sh

@@ -0,0 +1,4 @@
+rm -r ./target; lein clean
+lein uberjar
+java -jar -D"io.pedestal.log.defaultMetricsRecorder=nil" -D"io.pedestal.log.overrideLogger=nil" target/pedestal-standalone.jar
+

+ 7 - 4
frameworks/Clojure/pedestal/setup.sh

@@ -2,13 +2,16 @@
 
 
 fw_depends mysql java leiningen
 fw_depends mysql java leiningen
 
 
-sed -i 's|127.0.0.1:3306|'${DBHOST}':3306|g' src/pedestal/service.clj
+sed -i 's|127.0.0.1:3306|'${DBHOST}':3306|g' src/pedestal/pdg.clj
 
 
 lein clean
 lein clean
 
 
 rm -rf target
 rm -rf target
 # pack all dependencies into a single jar: target/pedestal-standalone.jar
 # pack all dependencies into a single jar: target/pedestal-standalone.jar
 lein uberjar
 lein uberjar
-# -server is much faster
-# 'lein run' passes '-client -XX:+TieredCompilation -XX:TieredStopAtLevel=1' which make it starts fast, but runs slow
-java -server -jar target/pedestal-standalone.jar &
+
+# java -jar target/pedestal-standalone.jar &
+# java -XX:+AggressiveOpts -jar -D"io.pedestal.log.defaultMetricsRecorder=nil" -D"io.pedestal.log.overrideLogger=nil" target/pedestal-standalone.jar &
+#java -jar -D"io.pedestal.log.defaultMetricsRecorder=nil" -D"io.pedestal.log.overrideLogger=nil" target/pedestal-standalone.jar > /dev/null 2>&1 &
+java -jar -D"io.pedestal.log.defaultMetricsRecorder=nil" -D"io.pedestal.log.overrideLogger=nil" target/pedestal-standalone.jar &
+

+ 336 - 0
frameworks/Clojure/pedestal/src/pedestal/pdg.clj

@@ -0,0 +1,336 @@
+(ns pedestal.pdg
+  (:gen-class)
+  (:require [io.pedestal.http :as http]
+            [io.pedestal.http.route :as route]
+            [io.pedestal.interceptor.chain :as chain]
+            ;[io.pedestal.http.request :as request]
+            ;[io.pedestal.http.body-params :as body-params]
+            [clojure.java.jdbc :as jdbc]
+            [cheshire.core :as json]
+            [hiccup.core :as hiccup]
+            [hiccup.util]
+            [hiccup.page])
+  (:import (java.nio ByteBuffer)
+           (java.nio.charset StandardCharsets)
+           (java.net InetSocketAddress)
+           (org.eclipse.jetty.server Request
+                                     Server)
+           (org.eclipse.jetty.server.handler AbstractHandler)
+           (org.eclipse.jetty.util Callback
+                                   BufferUtil)
+           (javax.servlet.http HttpServletRequest
+                               HttpServletResponse)
+           (com.zaxxer.hikari HikariConfig
+                              HikariDataSource)))
+
+;; This is based on the "Fast Pedestal" sample found in Pedestal's repo
+;;  You can see the details here: https://github.com/pedestal/pedestal/tree/master/samples/fast-pedestal
+
+;; THIS IS A BENCHMARK !!!
+;; -----------------------
+;; The global var settings here are strongly discouraged for production apps
+
+;; Auxiliary functions
+;; ---------------------------
+(defn sanitize-queries-param
+  "Sanitizes the `queries` parameter. Clamps the value between 1 and 500.
+  Invalid (string) values become 1."
+  [request]
+  (let [n ^long (try (Integer/parseInt (get-in request [:params :queries] "1"))
+                     (catch Exception e 1))] ; default to 1 on parse failure
+    (cond
+      (< ^long n 1) 1
+      (> ^long n 500) 500
+      :else n)))
+
+(defn json-buffer
+  [x]
+  (BufferUtil/toBuffer (json/generate-string x) StandardCharsets/UTF_8))
+
+(defn hikari-data-source []
+  (let [config (doto (HikariConfig.)
+                 (.setAutoCommit true)
+                 (.setReadOnly false)
+                 (.setConnectionTimeout 30000)
+                 (.setValidationTimeout 5000)
+                 (.setIdleTimeout 600000)
+                 (.setMaxLifetime 1800000)
+                 (.setMinimumIdle 10)
+                 (.setMaximumPoolSize 256)
+                 (.setPoolName "db-pool")
+                 (.setAllowPoolSuspension false)
+                 (.setUsername "benchmarkdbuser")
+                 (.setPassword "benchmarkdbpass")
+                 ;(.setDataSourceClassName "com.mysql.jdbc.jdbc2.optional.MysqlDataSource")
+                 ;(.setJdbcUrl "jdbc:mysql://127.0.0.1:3306/hello_world?username=benchmarkdbuser&password=benchmarkdbpass")
+                 (.setJdbcUrl "jdbc:mysql://127.0.0.1:3306/hello_world")
+                 (.setRegisterMbeans false))]
+    (HikariDataSource. config)))
+
+(def db (delay {:datasource (hikari-data-source)}))
+
+;; Handlers
+;; ---------------------------
+(def hello-buffer (BufferUtil/toBuffer "Hello, World!" StandardCharsets/UTF_8))
+(defn home-page
+  [request]
+  {:body (.slice ^ByteBuffer hello-buffer)
+   :headers {"Content-Type" "text/plain"}
+   :status 200})
+
+(def json-page-buffer (json-buffer {:message "Hello, World!"}))
+(def json-headers {"Content-Type" "application/json"})
+(defn json-page
+  [request]
+  {:body (.slice ^ByteBuffer json-page-buffer)
+   :headers json-headers
+   :status 200})
+
+(defn get-world-raw
+  [^long world-id]
+  (first (jdbc/query @db ["select * from world where id = ?" world-id])))
+
+(defn get-worlds-raw
+  "Run query repeatedly, return an array"
+  [n-queries]
+  (mapv get-world-raw (repeatedly n-queries #(unchecked-inc ^long (rand-int 9999)))))
+
+(defn get-all-fortunes-raw
+  "Query all Fortune records from the database using JDBC."
+  []
+  (jdbc/query @db ["select * from fortune"]))
+
+(defn get-fortunes
+  "Fetch the full list of Fortunes from the database, sort them by the fortune
+  message text, and then return the results."
+  []
+  (sort-by :message
+    (conj
+      (get-all-fortunes-raw)
+      {:id 0
+       :message "Additional fortune added at request time."})))
+
+(def fortunes-xf (map (fn [x]
+                        [:tr
+                         [:td (:fortune/id x)]
+                         [:td (hiccup.util/escape-html (:fortune/message x))]])))
+(defn fortunes-hiccup
+  "Render the given fortunes to simple HTML using Hiccup."
+  [fortunes]
+  (hiccup.page/html5
+    [:head
+     [:title "Fortunes"]]
+    [:body
+     (into [:table
+            [:tr
+             [:th "id"]
+             [:th "message"]]]
+           fortunes-xf
+           fortunes)]))
+
+(def base-fortune-pre "<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>")
+(def base-fortune-post "</table></body></html>")
+(defn fortunes-str
+  "The HTML bit for this is very very small;
+  Opt to create the HTML string by hand in a tight loop rather than using Hiccup"
+  [fortunes]
+  (let [sb (StringBuilder. ^String base-fortune-pre)]
+    (doseq [{:keys [id message]} fortunes]
+      (.append sb "<tr><td>")
+      (.append sb (str id))
+      (.append sb "</td><td>")
+      (dotimes [c-idx (count message)]
+        (let [c (.charAt ^String message c-idx)]
+          (case c
+            \& (.append sb "&amp;")
+            \" (.append sb "&quot;")
+            \' (.append sb "&apos;")
+            \< (.append sb "&lt;")
+            \> (.append sb "&gt;")
+            (.append sb c))))
+      (.append sb "</td></tr>"))
+    (.append sb base-fortune-post)
+    (.toString sb)))
+
+(defn update-and-persist-raw
+  "Using JDBC: Changes the :randomNumber of a number of world entities.
+  Persists the changes to sql then returns the updated entities"
+  [queries]
+  ;; TODO: This should be refactored to be smarter about transducers
+  (let [world (mapv #(assoc % :randomnumber (unchecked-inc ^long (rand-int 9999))) (get-worlds-raw queries))]
+    (doseq [{:keys [id randomnumber]} world]
+      (jdbc/update!
+       @db
+       :world {:randomnumber randomnumber}
+       ["id = ?" id]))
+    world))
+
+(defn single-query
+  "Test 2: Single database query"
+  [request]
+  {:body (json-buffer (get-world-raw (unchecked-inc ^long (rand-int 10000))))
+   :headers json-headers
+   :status 200})
+
+(defn multiple-queries
+  "Test 3: Multiple database queries"
+  [request]
+  (let [n (sanitize-queries-param request)
+        b (json-buffer (get-worlds-raw n))]
+    {:body b
+     ;:headers (assoc json-headers "Content-Length" (.position ^ByteBuffer b))
+     :headers json-headers
+     :status 200}))
+
+(defn fortune
+  "Test 4: Fortunes"
+  [request]
+  (let [^String s (fortunes-str (get-fortunes))]
+    {:body (BufferUtil/toBuffer s StandardCharsets/UTF_8)
+     :headers {"Content-Type" "text/html;charset=UTF-8"}
+     :status 200}))
+
+(defn db-updates
+  "Test 5: Database updates"
+  [request]
+  (let [n (sanitize-queries-param request)
+        b (json-buffer (update-and-persist-raw n))]
+    {:body b
+     ;:headers (assoc json-headers "Content-Length" (.position ^ByteBuffer b))
+     :headers json-headers
+     :status 200}))
+
+(def routes #{["/plaintext" :get `home-page]
+              ["/json"      :get `json-page]
+              ["/db"        :get `single-query]
+              ["/queries"   :get [route/query-params `multiple-queries]]
+              ["/fortunes"  :get `fortune]
+              ["/updates"   :get [route/query-params `db-updates]]})
+
+
+;; Usually we'd use the request/ContainerRequest protocol,
+;; but we're instead going to use direct interop to Jetty.
+;; We can just create something that is "request like" with only the required keys.
+;;
+;; We'll need a function to connect the Interceptor Chain to Jetty.
+;; In Pedestal, this piece is called a "Chain Provider"
+;; ----------------------------------------------------
+
+(defn direct-jetty-provider
+  "Given a service-map,
+  provide all necessary functionality to execute the interceptor chain,
+  including extracting and packaging the base :request into context.
+  These functions/objects are added back into the service map for use within
+  the server-fn.
+  See io.pedestal.http.impl.servlet-interceptor.clj as an example.
+
+  Interceptor chains:
+   * Terminate based on the list of :terminators in the context.
+   * Call the list of functions in :enter-async when going async.  Use these to toggle async mode on the container
+   * Will use the fn at :async? to determine if the chain has been operating in async mode (so the container can handle on the outbound)"
+  [service-map]
+  (let [interceptors (::http/interceptors service-map)]
+    ;; In this case, `proxy` will provide nearly the same class as `gen-class`
+    (assoc service-map ::handler (proxy [AbstractHandler] []
+                                   (handle [target ^Request base-request servlet-req servlet-resp]
+                                     ;; This is where you'd build a context and execute interceptors
+                                     (let [resp (.getResponse base-request)
+                                           initial-context {:request {:query-string     (.getQueryString base-request)
+                                                                      :request-method   (keyword (.toLowerCase ^String (.getMethod base-request)))
+                                                                      :body             (.getInputStream base-request)
+                                                                      :path-info        target ; Could also use (.getRequestURI base-request)
+                                                                      :async-supported? (.isAsyncSupported base-request)}}
+                                           resp-ctx (chain/execute initial-context
+                                                                       interceptors)]
+                                       (.setContentType resp (get-in resp-ctx [:response :headers "Content-Type"]))
+                                       ;(.setStatus resp (get-in resp-ctx [:response :status]))
+                                       (.setStatus resp 200)
+                                       ;; All of our bodies are unique ByteBuffers...
+                                       (.sendContent (.getHttpOutput resp)
+                                                     ^ByteBuffer (get-in resp-ctx [:response :body])
+                                                     ;Callback/NOOP ;; The payloads are so small, going non-blocking may cause additional overhead
+                                                     )
+                                       ;; If your bodies are static ByteBuffers, you'll need to slice them
+                                       ;(.sendContent (.getHttpOutput resp)
+                                       ;              (.slice ^ByteBuffer (get-in resp-ctx [:response :body])))
+                                       ;; If your bodies are strings, you can create ByteBuffers from them
+                                       ;; (.sendContent (.getHttpOutput resp)
+                                       ;;               (BufferUtil/toBuffer (get-in resp-ctx [:response :body] StandardCharsets/UTF_8))
+                                       ;;               ;Callback/NOOP ;; The payloads are so small, going non-blocking may cause additional overhead
+                                       (.setHandled base-request true)))))))
+
+(defn jetty-server-fn
+  "Given a service map (with interceptor provider established) and a server-opts map,
+  Return a map of :server, :start-fn, and :stop-fn.
+  Both functions are 0-arity"
+  [service-map server-opts]
+  (let [handler (::handler service-map)
+        {:keys [host port join?]
+         :or {host "127.0.0.1"
+              port 8080
+              join? false}} server-opts
+        addr (InetSocketAddress. ^String host ^int port)
+        server (doto (Server. addr)
+                 (.setHandler handler))]
+    {:server server
+     :start-fn (fn []
+                 (.start server)
+                 (when join? (.join server))
+                 server)
+     :stop-fn (fn []
+                (.stop server)
+                server)}))
+
+;; Consumed by peddemo.server/create-server
+;; See http/default-interceptors for additional options you can configure
+(def service
+  {:env :prod
+   ::http/interceptors [;http/log-request
+                        ;http/not-found
+                        ;route/query-params ;; We'll do this per-route
+                        ;(middleware/file-info)
+                        ;; We don't have any resources
+                        ;;  -- we could also use a ServletFilter to avoid calling
+                        ;;     into the application/service
+                        ;(middleware/resource "public")
+                        ;(route/method-param :_method)
+                        ;; The routes are static and compiled at startup.
+                        ;; We'll be using the MapTree router...
+                        (route/router (route/expand-routes routes))]
+   ::http/join? false
+   ;; Normally we'd use Pedestal's Servlet hook on Jetty,
+   ;; using the `::http/type :jetty` setting, but we want to use
+   ;; our the chain-provider optimized for our application...
+
+   ;; Call directly into Jetty, skipping Pedestal's Jetty Provider/options
+   ::http/type jetty-server-fn
+   ::http/chain-provider direct-jetty-provider
+
+
+   ::http/port (or (some-> (System/getenv "PORT")
+                           Integer/parseInt)
+                   8080)
+   ;; Our provider doesn't recognize these
+   ;::http/container-options {:h2c? true
+   ;                          :h2? false
+   ;                          ;:keystore "test/hp/keystore.jks"
+   ;                          ;:key-password "password"
+   ;                          ;:ssl-port 8443
+   ;                          :ssl? false
+   ;                          ;:context-configurator #(jetty-util/add-servlet-filter % {:filter DoSFilter})
+   ;                          }
+   })
+
+(defn -main [& args]
+  (println "Starting your server...")
+  (-> service
+      (assoc ::http/join? true)
+      http/create-server
+      http/start))
+
+(defn run-dev []
+  ;; TODO: We could add back all the dev interceptors here if we wanted
+  (-> service
+      http/create-server
+      http/start))
+

+ 9 - 9
frameworks/Clojure/pedestal/src/pedestal/service.clj

@@ -5,7 +5,7 @@
         hiccup.core
         hiccup.core
         hiccup.util
         hiccup.util
         hiccup.page)
         hiccup.page)
-  (:require [io.pedestal.http :as bootstrap]
+  (:require [io.pedestal.http :as http]
             [io.pedestal.http.route :as route]
             [io.pedestal.http.route :as route]
             [io.pedestal.http.body-params :as body-params]
             [io.pedestal.http.body-params :as body-params]
             [io.pedestal.http.route.definition :refer [defroutes]]
             [io.pedestal.http.route.definition :refer [defroutes]]
@@ -115,12 +115,12 @@
 (defn json-serialization
 (defn json-serialization
   "Test 1: JSON serialization"
   "Test 1: JSON serialization"
   [request]
   [request]
-  (bootstrap/json-response {:message "Hello, World!"}))
+  (http/json-response {:message "Hello, World!"}))
 
 
 (defn single-query-test
 (defn single-query-test
   "Test 2: Single database query"
   "Test 2: Single database query"
   [request]
   [request]
-  (bootstrap/json-response (first (run-queries 1))))
+  (http/json-response (first (run-queries 1))))
 
 
 (defn multiple-queries-test
 (defn multiple-queries-test
   "Test 3: Multiple database queries"
   "Test 3: Multiple database queries"
@@ -128,7 +128,7 @@
   (-> request
   (-> request
       (sanitize-queries-param)
       (sanitize-queries-param)
       (run-queries)
       (run-queries)
-      (bootstrap/json-response)))
+      (http/json-response)))
 
 
 (defn fortune-test
 (defn fortune-test
   "Test 4: Fortunes"
   "Test 4: Fortunes"
@@ -146,7 +146,7 @@
   (-> request
   (-> request
       (sanitize-queries-param)
       (sanitize-queries-param)
       (update-and-persist)
       (update-and-persist)
-      (bootstrap/json-response)))
+      (http/json-response)))
 
 
 
 
 (defn plaintext
 (defn plaintext
@@ -168,7 +168,7 @@
 (def service
 (def service
   "How the server will look, not the code to start it up"
   "How the server will look, not the code to start it up"
   { :env :prod
   { :env :prod
-    ::bootstrap/routes routes
-    ::bootstrap/resource-path "/public"
-    ::bootstrap/type :jetty
-    ::bootstrap/port 8080})
+    ::http/routes routes
+    ::http/resource-path "/public"
+    ::http/type :jetty
+    ::http/port 8080})

+ 2 - 2
frameworks/Clojure/pedestal/test/pedestal/service_test.clj

@@ -1,11 +1,11 @@
 (ns pedestal.service-test
 (ns pedestal.service-test
   (:require [clojure.test :refer :all]
   (:require [clojure.test :refer :all]
             [io.pedestal.test :refer :all]
             [io.pedestal.test :refer :all]
-            [io.pedestal.http :as bootstrap]
+            [io.pedestal.http :as http]
             [pedestal.service :as service]))
             [pedestal.service :as service]))
 
 
 (def service
 (def service
-  (::bootstrap/service-fn (bootstrap/create-servlet service/service)))
+  (::http/service-fn (http/create-servlet service/service)))
 
 
 (deftest home-page-test
 (deftest home-page-test
   (is (=
   (is (=