text:
(ns play-clj-doclet.html
  (:require [clojure.java.io :as io]
            [clojure.string :as string]
            [hiccup.core :refer :all]))

(defn str->filename
  [ns s]
  (-> (subs ns (+ (.lastIndexOf ns ".") 1))
      (str "." s)
      (string/replace "?" "_q")
      (string/replace "->" "_")
      (string/replace ">" "_r")
      (string/replace "<" "_l")
      (str ".html")))

(defn sidebar
  [parsed-files]
  [:div {:class "sidebar"}
   (for [[ns groups] parsed-files]
     (cons [:div {:class "ns"} ns]
           (for [{:keys [name]} groups]
             [:div {:class "name"}
              [:a {:href (str->filename ns name) :target "content-frame" :onClick (str "setHash(this)") :newHash (str->filename ns name)}
               name]])))])

(defn java-param
  [[type-name param-name]]
  (html [:span {:class "j-arg"}
         [:span {:class "j-type"} "^" type-name]
         " "
         param-name]))

(defn java-item
  [{:keys [name text type args]}]
  [:div {:class "j-item"}
   [:span {:class "j-name"}
    (str name)]
   [:span {:class "j-args"}
    (string/join ", " (map java-param args))]
   (when text
     [:div {:class "j-doc"} text])])

(defn content
  [{:keys [name docstring arglists java raw raw*]} & [home-link-hash]]
  [:div {:class "content"}
   [:div {:class "item"}
    [:div {:class "clj"}
     (for [args arglists]
       [:div {:class "c-head"}
        (if home-link-hash
          [:a {:href (str "index.html" (str "#" home-link-hash))
               :target "_top"}
           (pr-str args)]
          (pr-str args))])
     [:div {:class "c-doc"} docstring]]
    (when (> (count java) 0)
      (list [:div {:class "c-head"} "Options"]
            (for [[item-name {:keys [text items]}] java]
              (list (when (> (count java) 1)
                      [:div {:class "j-text"} text])
                    [:div {:class "java"}
                     (map java-item items)]))))
    [:div {:class "c-head"} "Source"]
    [:div {:class "c-src"}
     (when raw* [:pre raw*])
     [:pre raw]]]])

(defn create-site-file
  ([name content]
   (html [:html
          [:head
           [:title name]
           [:script {:src "nav.js"}]
           [:link {:rel "stylesheet" :href "highlight.css"}]
           [:link {:rel "stylesheet" :href "main.css"}]]
          [:body
           content
           [:script {:src "highlight.js"}]
           [:script {:src "main.js"}]]])))

(defn create-embed-file
  [content]
  (html [:html [:body content]]))

(defn copy-from-res
  [dir file-name]
  (spit (io/file dir file-name)
        (-> file-name io/resource slurp)))

(defn index-frameset
  []
  (html
   [:html
    [:head
     [:title "play-clj docs"]
     [:script {:src "nav.js"}]
     [:link {:rel "stylesheet" :href "highlight.css"}]
     [:link {:rel "stylesheet" :href "main.css"}]]
    [:frameset {:cols "250px,100%" :onLoad "goToHash();"}
     [:frame {:src "sidebar.html"}]
     [:frame {:src "blank.html" :name "content-frame" :id "content-frame"}]
     [:script {:src "highlight.js"}]
     [:script {:src "main.js"}]]]))

(defn create-site!
  [dir parsed-files]
  (.mkdir (io/file dir))
  (copy-from-res dir "main.css")
  (copy-from-res dir "main.js")
  (copy-from-res dir "nav.js")
  (copy-from-res dir "highlight.css")
  (copy-from-res dir "highlight.js")
  (doseq [[ns groups] parsed-files]
    (doseq [{:keys [name] :as group} groups]
      (spit (io/file dir (str->filename ns name))
            (create-site-file name (content group (str->filename ns name))))))
  (spit (io/file dir "sidebar.html")
        (create-site-file "sidebar" (sidebar parsed-files)))
  (spit (io/file dir "blank.html")
        (create-site-file "blank" nil))
  (spit (io/file dir "index.html")
        (index-frameset))
  (println "Created" (str dir "/")))

(defn create-embed!
  [dir parsed-files]
  (.mkdir (io/file dir))
  (doseq [[ns groups] parsed-files]
    (doseq [{:keys [name] :as group} groups]
      (spit (io/file dir (str->filename ns name))
            (create-embed-file (content group)))))
  (spit (io/file dir "index.edn")
        (->> parsed-files
             (map (fn [[ns groups]]
                    (for [{:keys [name]} groups]
                      {:ns ns
                       :name name
                       :file (str->filename ns name)})))
             (apply concat)
             vec
             pr-str))
  (println "Created" (str dir "/")))


text:
(ns play-clj-doclet.core
  (:require [clojure.edn :as edn]
            [clojure.java.io :as io]
            [clojure.string :as string]
            [marginalia.core :as marg]
            [markdown.core :as m]
            [play-clj-doclet.html :as html])
  (:import [com.sun.javadoc ClassDoc ConstructorDoc Doc ExecutableMemberDoc
            FieldDoc MethodDoc Parameter RootDoc]))

(def ^:const default-ns "play-clj.core")
(def ^:const ignore-files #{"physics.clj" "utils.clj"})

(def classes (-> "classes.edn" io/resource slurp edn/read-string))

(defn string->keyword
  [s case-type]
  (->> (case case-type
         :upper #"_"
         :pascal #"(?<=.)(?=[A-Z])"
         :camel #"(?=[A-Z])"
         (throw (Exception. "Unrecognized case type.")))
       (string/split s)
       (map string/lower-case)
       (string/join "-")
       keyword))

(defn parse-param
  [^Parameter p]
  [(.typeName p) (-> (.name p) (string->keyword :camel) name)])

(defn after-last-period
  [^String s]
  (subs s (+ 1 (.lastIndexOf s "."))))

(defn parse-doc-name
  [^String doc-name doc-type]
  (case doc-type
    :constructors
    nil

    :classes
    (string->keyword (after-last-period doc-name) :pascal)

    :static-classes
    (string->keyword (after-last-period doc-name) :pascal)

    :static-fields-upper
    (string->keyword doc-name :upper)

    :static-fields-pascal
    (string->keyword doc-name :pascal)

    :static-fields-camel
    (string->keyword doc-name :camel)

    ; else
    (string->keyword doc-name :camel)))

(defn parse-doc
  [^Doc d clj-name doc-type]
  (merge {}
         {:name (->> [(second (string/split clj-name #" "))
                      (parse-doc-name (.name d) doc-type)]
                     (remove nil?)
                     (string/join " "))}
         (when (> (count (.commentText d)) 0)
           {:text (.commentText d)})
         (when (and (isa? (type d) MethodDoc)
                    (not= (-> d .returnType .typeName) "void"))
           {:type (-> d .returnType .typeName)})
         (cond
           (isa? (type d) ExecutableMemberDoc)
           {:args (->> d .parameters (map parse-param) vec)}
           (and (isa? (type d) FieldDoc) (not (.isStatic d)))
           {:args [[(-> d .type .typeName) "value"]]})))

(defn parse-class-entry
  [^ClassDoc c [clj-name doc-type]]
  (some->> (case doc-type
             :methods (filter #(-> % .isStatic not) (.methods c))
             :static-methods (filter #(.isStatic %) (.methods c))
             :fields (filter #(-> % .isStatic not) (.fields c))
             :static-fields-upper (filter #(.isStatic %) (.fields c))
             :static-fields-pascal (filter #(.isStatic %) (.fields c))
             :static-fields-camel (filter #(.isStatic %) (.fields c))
             :classes (filter #(-> % .isStatic not) (.innerClasses c))
             :static-classes (filter #(.isStatic %) (.innerClasses c))
             :constructors (filter #(-> % .isStatic not) (.constructors c))
             nil)
           (filter #(.isPublic %))
           (map #(parse-doc % clj-name doc-type))
           (concat (when-let [sc (.superclass c)]
                     (when (not= (.typeName sc) "Object")
                       (parse-class-entry sc [clj-name type]))))
           distinct
           (sort-by :name)
           vec))

(defn parse-class
  [^ClassDoc c]
  (some->> (or (get classes (.qualifiedTypeName c))
               (get classes (.typeName c)))
           (map #(vector (first (string/split (first %) #" "))
                         {:text (.commentText c)
                          :items (parse-class-entry c %)}))))

(defn remove-destructuring
  [arglist]
  (map #(or (:as %) %) arglist))

(defn arglists
  [form]
  (->> (or (some-> (some #(if (vector? %) %) form) list)
           (map first (filter list? form)))
       (map #(cons (second form) %))
       (map remove-destructuring)
       vec))

(defn process-group
  [ns {:keys [type raw docstring] :as group} java-docs]
  (let [form (read-string raw)
        n (second form)]
    (when (and (contains? #{'defn 'defmacro} (first form))
               (-> n meta :private not))
      (assoc group
             :ns (if (> (count ns) 0) ns default-ns)
             :name (str n)
             :java (->> java-docs
                        (filter #(= (first %) (str n)))
                        (sort-by first)
                        vec)
             :arglists (arglists form)
             :docstring (m/md-to-html-string docstring)))))

(defn merge-groups
  [groups]
  (for [{:keys [name] :as group} groups]
    (when (and name (not (.endsWith name "*")))
      (->> groups
           (some #(if (= (:name %) (str name "*")) %))
           :raw
           (assoc group :raw*)))))

(defn process-groups
  [{:keys [ns groups] :as parsed-file} java-docs]
  (->> groups
       (map #(process-group ns % java-docs))
       merge-groups
       (remove nil?)))

(defn parse-clj
  [java-docs files]
  (->> files
       (filter #(-> % .getName (.endsWith ".clj")))
       (remove #(contains? ignore-files (.getName %)))
       (sort-by #(.getName %))
       (map #(.getCanonicalPath %))
       (map marg/path-to-doc)
       (map #(process-groups % java-docs))
       (apply concat)
       (group-by :ns)
       (into (sorted-map))))

(defn generate!
  [java-docs]
  (let [play-clj-files (file-seq (io/file "../src/"))
        nightmod-file (io/file "../../Nightmod/src/clojure/nightmod/game.clj")
        net-file (io/file "../../play-clj.net/src/play_clj/net.clj")]
    (->> play-clj-files
         (parse-clj java-docs)
         (html/create-site! "site"))
    (when (and (.exists nightmod-file) (.exists net-file))
      (->> play-clj-files
           (concat [nightmod-file net-file])
           (parse-clj java-docs)
           (html/create-embed! "embed")))))

(defn generate-all!
  [^RootDoc root]
  (if (= 0 (count (.classes root)))
    (println "No Java classes found")
    (->> (map parse-class (.classes root))
         (filter some?)
         (apply concat)
         generate!)))

repo_name: oakes/play-clj

repo_language: Clojure

mime_type: text/plain


text:
package play_clj_doclet.core;

import clojure.lang.RT;
import clojure.lang.Symbol;
import clojure.lang.Var;

import com.sun.javadoc.*;

public class Start {
	public static boolean start(RootDoc root) {
		RT.var("clojure.core", "require").invoke(Symbol.intern("play-clj-doclet.core"));
		Var generate = RT.var("play-clj-doclet.core", "generate-all!");
		generate.invoke(root);
		return true;
	}
}


text:
{"Actor" {"actor!" :methods}
 "ActorGestureListener" {"actor-gesture-listener!" :methods}
 "Align" {"align" :static-fields-camel}
 "com.badlogic.gdx.graphics.g2d.Animation" {"animation" :methods
                                            "animation!" :methods}
 "Animation.PlayMode" {"play-mode" :static-fields-upper}
 "AnimationController" {"animation-controller" :methods
                        "animation-controller!" :methods}
 "Application" {"app!" :methods}
 "Array" {"gdx-array" :methods
          "gdx-array!" :methods}
 "ArrayMap" {"gdx-array-map" :methods
             "gdx-array-map!" :methods}
 "AssetManager" {"asset-manager" :methods
                 "asset-manager!" :methods}
 "Audio" {"audio!" :methods}
 "Bezier" {"bezier" :methods
           "bezier!" :methods}
 "BitmapFont" {"bitmap-font" :methods
               "bitmap-font!" :methods}
 "BlendingAttribute" {"attribute :blending" :constructors
                      "attribute-type :blending" :static-fields-pascal
                      "attribute! :blending" :static-methods}
 "Body" {"body!" :methods}
 "BodyDef" {"body-def" :fields}
 "BoundingBox" {"bounding-box" :methods
                "bounding-box!" :methods}
 "Bresenham2" {"bresenham-2" :methods
               "bresenham-2!" :methods}
 "BSpline" {"b-spline" :methods
            "b-spline!" :methods}
 "Button.ButtonStyle" {"style :button" :constructors}
 "CatmullRomSpline" {"catmull-rom-spline" :methods
                     "catmull-rom-spline!" :methods}
 "Circle" {"circle" :methods
           "circle!" :methods}
 "CircleMapObject" {"map-object :circle" :methods}
 "ChangeListener" {"change-listener!" :methods}
 "ClickListener" {"click-listener!" :methods}
 "Color" {"color" :static-fields-upper
          "color!" :methods}
 "ColorAttribute" {"attribute :color" :constructors
                   "attribute-type :color" :static-fields-pascal
                   "attribute! :color" :static-methods}
 "ConvexHull" {"convex-hull" :methods
               "convex-hull!" :methods}
 "ChainShape" {"chain-shape" :methods
               "chain-shape!" :methods}
 "CheckBox" {"check-box" :methods
             "check-box!" :methods}
 "CheckBox.CheckBoxStyle" {"style :check-box" :constructors}
 "CircleShape" {"circle-shape" :methods
                "circle-shape!" :methods}
 "CubemapAttribute" {"attribute :cubemap" :constructors
                     "attribute-type :cubemap" :static-fields-pascal
                     "attribute! :cubemap" :static-methods}
 "DelaunayTriangulator" {"delaunay-triangulator" :methods
                         "delaunay-triangulator!" :methods}
 "DepthTestAttribute" {"attribute :depth-test" :constructors
                       "attribute-type :depth-test" :static-fields-pascal
                       "attribute! :depth-test" :static-methods}
 "Dialog" {"dialog" :methods
           "dialog!" :methods}
 "DistanceJointDef" {"joint-def :distance" :fields}
 "DragListener" {"drag-listener!" :methods}
 "EarClippingTriangulator" {"ear-clipping-triangulator" :methods
                            "ear-clipping-triangulator!" :methods}
 "EdgeShape" {"edge-shape" :methods
              "edge-shape!" :methods}
 "Ellipse" {"ellipse" :methods
            "ellipse!" :methods}
 "EllipseMapObject" {"map-object :ellipse" :methods}
 "Environment" {"environment" :methods
                "environment!" :methods}
 "Files" {"files!" :methods}
 "Fixture" {"fixture!" :methods}
 "FixtureDef" {"fixture-def" :fields}
 "FloatAttribute" {"attribute :float" :constructors
                   "attribute-type :float" :static-fields-pascal
                   "attribute! :float" :static-methods}
 "FloatCounter" {"float-counter" :methods
                 "float-counter!" :methods}
 "FocusListener" {"focus-listener!" :methods}
 "FrictionJointDef" {"joint-def :friction" :fields}
 "Frustum" {"frustum" :methods
            "frustum!" :methods}
 "GearJointDef" {"joint-def :gear" :fields}
 "GeometryUtils" {"geometry!" :static-methods}
 "GestureDetector" {"gesture-detector!" :methods}
 "Graphics" {"graphics!" :methods}
 "GridPoint2" {"grid-point-2" :methods
               "grid-point-2!" :methods}
 "GridPoint3" {"grid-point-3" :methods
               "grid-point-3!" :methods}
 "GL20" {"gl!" :methods
         "gl" :static-fields-upper}
 "HexagonalTiledMapRenderer" {"hexagonal-tiled-map" :methods
                              "hexagonal-tiled-map!" :methods}
 "HorizontalGroup" {"horizontal" :methods
                    "horizontal!" :methods}
 "Image" {"image" :methods
          "image!" :methods}
 "ImageButton" {"image-button" :methods
                "image-button!" :methods}
 "ImageButton.ImageButtonStyle" {"style :image-button" :constructors}
 "ImageTextButton" {"image-text-button" :methods
                    "image-text-button!" :methods}
 "ImageTextButton.ImageTextButtonStyle" {"style :image-text-button" :constructors}
 "Input" {"input!" :methods}
 "Input.Keys" {"key-code" :static-fields-upper
               "key-pressed?" :static-fields-upper}
 "Input.Buttons" {"button-code" :static-fields-upper
                  "button-pressed?" :static-fields-upper}
 "InputProcessor" {"input-processor!" :methods}
 "Interpolation" {"interpolation" :static-classes}
 "Intersector" {"intersector!" :static-methods}
 "IntAttribute" {"attribute :int" :constructors
                 "attribute-type :int" :static-fields-pascal
                 "attribute! :int" :static-methods}
 "IsometricStaggeredTiledMapRenderer" {"isometric-staggered-tiled-map" :methods
                                       "isometric-staggered-tiled-map!" :methods}
 "IsometricTiledMapRenderer" {"isometric-tiled-map" :methods
                              "isometric-tiled-map!" :methods}
 "Joint" {"joint!" :methods}
 "Label" {"label" :methods
          "label!" :methods}
 "Label.LabelStyle" {"style :label" :constructors}
 "MapLayer" {"map-layer" :methods
             "map-layer!" :methods}
 "MapLayers" {"map-layers" :methods
              "map-layers!" :methods}
 "MapObject" {"map-object!" :methods}
 "MapObjects" {"map-objects" :methods
               "map-objects!" :methods}
 "Material" {"material" :methods
             "material!" :methods}
 "MathUtils" {"math!" :static-methods}
 "Matrix3" {"matrix-3" :methods
            "matrix-3!" :methods}
 "Matrix4" {"matrix-4" :methods
            "matrix-4!" :methods}
 "ModelBatch" {"model-batch" :methods
               "model-batch!" :methods}
 "ModelBuilder" {"model-builder" :methods
                 "model-builder!" :methods}
 "ModelInstance" {"model" :constructors
                  "model!" :methods}
 "MouseJointDef" {"joint-def :mouse" :fields}
 "Music" {"music" :methods
          "music!" :methods}
 "Net" {"net!" :methods}
 "NinePatch" {"nine-patch" :methods
              "nine-patch!" :methods}
 "NinePatchDrawable" {"drawable :nine-patch" :constructors}
 "OrthogonalTiledMapRenderer" {"orthogonal-tiled-map" :methods
                               "orthogonal-tiled-map!" :methods}
 "OrthographicCamera" {"orthographic" :methods
                       "orthographic!" :methods}
 "ParticleEffect" {"particle-effect" :methods
                   "particle-effect!" :methods}
 "PerspectiveCamera" {"perspective" :methods
                      "perspective!" :methods}
 "Pixmap" {"pixmap" :methods
           "pixmap!" :methods}
 "Pixmap.Format" {"pixmap-format" :static-fields-pascal}
 "Plane" {"plane" :methods
          "plane!" :methods}
 "Plane.PlaneSide" {"plane-side" :static-fields-pascal}
 "Polygon" {"polygon" :methods
            "polygon!" :methods}
 "PolygonMapObject" {"map-object :polygon" :methods}
 "PolylineMapObject" {"map-object :polyline" :methods}
 "PolygonShape" {"polygon-shape" :methods
                 "polygon-shape!" :methods}
 "Polyline" {"polyline" :methods
             "polyline!" :methods}
 "Preferences" {"pref!" :methods}
 "PrismaticJointDef" {"joint-def :prismatic" :fields}
 "ProgressBar.ProgressBarStyle" {"style :progress-bar" :constructors}
 "PulleyJointDef" {"joint-def :pulley" :fields}
 "Quaternion" {"quaternion" :methods
               "quaternion!" :methods}
 "Ray" {"ray" :methods
        "ray!" :methods}
 "Rectangle" {"rectangle" :methods
              "rectangle!" :methods}
 "RectangleMapObject" {"map-object :rectangle" :methods}
 "RevoluteJointDef" {"joint-def :revolute" :fields}
 "RopeJointDef" {"joint-def :rope" :fields}
 "Scaling" {"scaling" :static-fields-camel}
 "ScrollPane" {"scroll-pane" :methods
               "scroll-pane!" :methods}
 "ScrollPane.ScrollPaneStyle" {"style :scroll-pane" :constructors}
 "Segment" {"segment" :methods
            "segment!" :methods}
 "SelectBox" {"select-box" :methods
              "select-box!" :methods}
 "SelectBox.SelectBoxStyle" {"style :select-box" :constructors}
 "ShapeRenderer" {"shape" :methods
                  "shape!" :methods}
 "ShapeRenderer.ShapeType" {"shape-type" :static-fields-pascal}
 "Skin" {"skin" :methods
         "skin!" :methods}
 "Slider" {"slider" :methods
           "slider!" :methods}
 "Sound" {"sound" :methods
          "sound!" :methods}
 "Sphere" {"sphere" :methods
           "sphere!" :methods}
 "SplitPane.SplitPaneStyle" {"style :split-pane" :constructors}
 "SpriteDrawable" {"drawable :sprite" :constructors}
 "Stack" {"stack" :methods
          "stack!" :methods}
 "Stage" {"stage" :methods
          "stage!" :methods}
 "Table" {"table" :methods
          "table!" :methods}
 "TextureAtlas" {"texture-atlas" :methods
                 "texture-atlas!" :methods}
 "TextureAttribute" {"attribute :texture" :constructors
                     "attribute-type :texture" :static-fields-pascal
                     "attribute! :texture" :static-methods}
 "TextButton" {"text-button" :methods
               "text-button!" :methods}
 "TextButton.TextButtonStyle" {"style :text-button" :constructors}
 "TextField" {"text-field" :methods
              "text-field!" :methods}
 "TextField.TextFieldStyle" {"style :text-field" :constructors}
 "TextureMapObject" {"map-object :texture" :methods}
 "TextureRegion" {"texture" :methods
                  "texture!" :methods}
 "TextureRegionDrawable" {"drawable :texture-region" :constructors}
 "Tree" {"tree" :methods
         "tree!" :methods}
 "Touchpad.TouchpadStyle" {"style :touchpad" :constructors}
 "TiledMap" {"tiled-map" :methods
             "tiled-map!" :methods}
 "TiledMapTileLayer" {"tiled-map-layer" :methods
                      "tiled-map-layer!" :methods}
 "TiledMapTileLayer.Cell" {"tiled-map-cell" :methods
                           "tiled-map-cell!" :methods}
 "Tree.TreeStyle" {"style :tree" :constructors}
 "Vector2" {"vector-2" :methods
            "vector-2!" :methods}
 "Vector3" {"vector-3" :methods
            "vector-3!" :methods}
 "VertexAttributes.Usage" {"usage" :static-fields-pascal}
 "VerticalGroup" {"vertical" :methods
                  "vertical!" :methods}
 "WeldJointDef" {"joint-def :weld" :fields}
 "WheelJointDef" {"joint-def :wheel" :fields}
 "Window" {"window" :methods
           "window!" :methods}
 "Window.WindowStyle" {"style :window" :constructors}
 "WindowedMean" {"windowed-mean" :methods
                 "windowed-mean!" :methods}
 "World" {"box-2d" :methods
          "box-2d!" :methods}}


text:
function goToHash() {
	if (window.location.hash != "") {
		document.getElementById("content-frame").src = window.location.hash.substr(1)
	}
}

function setHash(link) {
	window.parent.location.hash = link.getAttribute("newHash")
}


text:
body {
	font-family: "Fira Sans", "Source Sans Pro", "Helvetica Neue", sans-serif;
	background-color: #293134;
	color: #c1cbc2;
	font-size: 16px;
	line-height: 1.4em;
}

a {
	transition-property: color;
	transition-duration: 200ms;
	transition-timing-function: cubic-bezier(.53,.4,.46,.75);
	color: #f2ecd7;
	box-shadow: 0 2px 0 0 rgba(255,255,255,0.1);
	text-decoration: none;
}

a:hover {
	color: #f2dc77;
}

.sidebar {
	transition-property: opacity;
	transition-duration: 200ms;
	transition-timing-function: cubic-bezier(.53,.4,.46,.75);
	opacity: 0.6;
}

.sidebar:hover {
	opacity: 0.9;
}

.ns {
	font-size: 24px;
	padding: 20px 0 0px 0;
	margin-bottom: 10px;
	display: inline-block;
	color: #ccf;
}

.name {
	margin-top: 5px;
	margin-bottom: 5px;
	text-indent: 10px;
}

.content {
	padding: 20px 0px 20px 20px;
}

.content code {
	box-shadow: 0 0 0 1px rgba(255,255,255,0.2);
	border-radius: 3px;
	padding: 0px 3px;
	background: rgba(255,255,255,0.1);
	text-shadow: 0 1px 0 rgba(0,0,0,0.5);
}

.content code, .content pre {
	font-family: "Fira Mono", "Source Code Poro", "Consolas", "Menlo", monospace;
}

.item {

}

.clj {
	margin-bottom: 2em;
}

.c-head {
	font-size: 20px;
	margin-bottom: 1em;
	color: #ccf;
	font-size: 1.5em;
}

.c-doc {
}

.c-src {
	margin-bottom: 2em;
}

.java {
	margin-bottom: 2em;
}

.j-text {
	margin-bottom: 5px;
}

.j-item * {
	transition-property: opacity;
	transition-duration: 200ms;
	transition-timing-function: cubic-bezier(.53,.4,.46,.75);
}

.j-item {
	margin-bottom: 0px;
	padding: 0.8em 0 0.8em 0;
	line-height: 1.4em;
	background: rgba(0, 0, 0, 0.15);
}
.j-item:nth-child(2n) {
	background: rgba(0, 0, 0, 0.25);
}
.j-name {
	color: #69e;
	text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.8);
	margin: 0 0px 0 20px;
	display: inline-block;
	font-size: 1.4em;
	line-height: 1.4em;
	border-right: 5px transparent solid;
	box-shadow: 0 2px 0 0 rgba(150,200,255,0.08);
}

.j-args {
	padding-left: 1em;
	opacity: 0.4;
	display: inline-block;
}

.j-item:hover .j-args {
	opacity: 0.95;
}

.j-arg {

}

.j-type {
	opacity: 0.5;
}

.j-doc {
	display: block;
	padding: 17px 20px 0px 20px;
	text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.5);
}


text:
var elems = document.getElementsByTagName("pre");
for (var i = 0; i < elems.length; i++) {
	hljs.highlightBlock(elems[i]);
}


text:
/**
 * Obsidian style
 * ported by Alexander Marenin (http://github.com/ioncreature)
 */

.hljs {
  display: block;
  overflow-x: auto;
  padding: 0.5em;
  background: #282b2e;
  -webkit-text-size-adjust: none;
}

.hljs-keyword,
.hljs-literal,
.hljs-change,
.hljs-winutils,
.hljs-flow,
.nginx .hljs-title,
.css .hljs-id,
.tex .hljs-special {
  color: #93c763;
}

.hljs-number {
  color: #ffcd22;
}

.hljs {
  color: #e0e2e4;
}

.css .hljs-tag,
.css .hljs-pseudo {
  color: #d0d2b5;
}

.hljs-attribute,
.hljs .hljs-constant {
  color: #668bb0;
}

.xml .hljs-attribute {
  color: #b3b689;
}

.xml .hljs-tag .hljs-value {
  color: #e8e2b7;
}

.hljs-code,
.hljs-class .hljs-title,
.hljs-header {
  color: white;
}

.hljs-class,
.hljs-hexcolor {
  color: #93c763;
}

.hljs-regexp {
  color: #d39745;
}

.hljs-at_rule,
.hljs-at_rule .hljs-keyword {
  color: #a082bd;
}

.hljs-doctype {
  color: #557182;
}

.hljs-link_url,
.hljs-tag,
.hljs-tag .hljs-title,
.hljs-bullet,
.hljs-subst,
.hljs-emphasis,
.hljs-type,
.hljs-preprocessor,
.hljs-pragma,
.ruby .hljs-class .hljs-parent,
.hljs-built_in,
.django .hljs-template_tag,
.django .hljs-variable,
.smalltalk .hljs-class,
.hljs-javadoc,
.django .hljs-filter .hljs-argument,
.smalltalk .hljs-localvars,
.smalltalk .hljs-array,
.hljs-attr_selector,
.hljs-pseudo,
.hljs-addition,
.hljs-stream,
.hljs-envvar,
.apache .hljs-tag,
.apache .hljs-cbracket,
.tex .hljs-command,
.hljs-prompt {
  color: #8cbbad;
}

.hljs-string {
  color: #ec7600;
}

.hljs-comment,
.hljs-annotation,
.hljs-blockquote,
.hljs-horizontal_rule,
.hljs-decorator,
.hljs-pi,
.hljs-deletion,
.hljs-shebang,
.apache .hljs-sqbracket,
.tex .hljs-formula {
  color: #818e96;
}

.hljs-keyword,
.hljs-literal,
.css .hljs-id,
.hljs-phpdoc,
.hljs-dartdoc,
.hljs-title,
.hljs-header,
.hljs-type,
.vbscript .hljs-built_in,
.rsl .hljs-built_in,
.smalltalk .hljs-class,
.diff .hljs-header,
.hljs-chunk,
.hljs-winutils,
.bash .hljs-variable,
.apache .hljs-tag,
.tex .hljs-special,
.hljs-request,
.hljs-at_rule .hljs-keyword,
.hljs-status {
  font-weight: bold;
}

.coffeescript .javascript,
.javascript .xml,
.tex .hljs-formula,
.xml .javascript,
.xml .vbscript,
.xml .css,
.xml .hljs-cdata {
  opacity: 0.5;
}


text:
![SetVer Logo](https://raw.githubusercontent.com/RocketRace/setver/main/logo.png)

The modern landscape of software development is hectic and unforgiving. Dependency hell, management hell, Jira (which is a hell of its own)... It's impossible to maintain order — or to maintain a project — without a sufficiently rigid foundation, else your whole team crumbles apart. You need to pick something solid to base your project on... So *why not pick the most solid foundation of them all?* ***Why not pick set theory?***

--------

# Setaceous Versioning (version `{{}}`)

> Setaceous: resembling a bristle in form or texture

**Setaceous Versioning (SetVer)** is a versioning scheme designed to revolutionize the world of software development. It is based on the purest, most* foundational mathematical object, the set. Under SetVer, your projects don't get a version number — they get a **version set**. When beginning your project, assign `{}` as your version set. Each time your project updates, add a new element to your version set. Version sets can be compared very elegantly, using simply the subset relation `⊆`.

# Here's a bunch of definitions and prose

## Version Sets

A **version set** represents a set containing zero or more arbitrarily nested unordered sets, with the restriction that every element inside a set is unique. These are represented with the brace characters `{` (U+007B) and `}` (U+007D). Elements of sets are delimited by an empty string, i.e. concatenated. Below is a regular expression (using PCRE) that parses valid version sets:

```regex
^({(?:((?1))(?!(?1)*\2))*})$
```

"Thank you, Olivia! It's all so clear to me now," you say. Well, you're welcome! But in case you aren't welcome, here are some examples of valid and invalid version sets.

Valid:
+ `{}`
+ `{{}}`
+ `{{{}}{}{{{}}}}`
+ `{{{{}{{}}}{{}}}{}}`

Invalid:
- `{`
- `{{{}}}}`
- `{{}{}}`
- `{{{}{}}{}}`

The **initial version set** of a project should be `{}`. This is analogous to a "version zero" in other less elegant versioning schemes (gross).

For all the mathematicians in the room, SetVer uses ZF as its model of set theory. The use of Choice is undefined behavior and can evoke nasal demons (but can also do your laundry).

## Version Comparison

Two version sets are compared using the subset relation `⊆` defined on the sets represented by a version set. That is, for any version sets `X` and `Y`, we say that `X ⊆ Y` if every element (i.e. immediate child) of the set represented by `X` is also an element of the set represented by `Y`. The `⊆` relation is a partial ordering on sets, which gives us quite a few useful properties:

* For any version set `X`, `X ⊆ X`.
* For any version sets `X` and `Y`, if `X ⊆ Y` and `Y ⊆ X`, then `X` and `Y` must be the same set.
* For any version sets `X`, `Y`, and `Z`, if `X ⊆ Y` and `Y ⊆ Z`, then `X ⊆ Z`.

It must be noted that `⊆` is not a total order over the class of sets. This means that there exists a pair of version sets `A` and `B` such that neither `A ⊆ B` nor `B ⊆ A`. In fact, there are infinitely many such pairs of version sets! (Proof is left as an exercise to the reader.) *However,* when a programmer is faced with an unwanted quirk in the system, they simply reframe the bug as a feature! That is to say, the existence of incomparable version sets is cool and good, actually. More on that later.

## Version Comparison Comparison

When two version sets `X` and `Y` are compared, there are four possible outcomes to the situation:

* `X ⊆ Y` and `Y ⊆ X`, in which case we say `X = Y`.
* `X ⊆ Y` but not `Y ⊆ X`, in which case we say `X ⊂ Y`.
* `Y ⊆ X` but not `X ⊆ Y`, in which case we say `X ⊃ Y`.
* Neither `X ⊆ Y` nor `Y ⊆ X`, in which case we say `X ⫓̸ Y`.

These four outcomes (`⊂`, `=`, `⊃` and `⫓̸`) can *also* be compared under a partial order, with the following rules:

| Row ≤ column | `⊂` | `=` | `⊃` | `⫓̸` |
| ------------:|:---:|:---:|:---:|:---:|
|          `⊂` | ✅ | ✅ | ✅ | ❌ |
|          `=` | ❌ | ✅ | ✅ | ❌ |
|          `⊃` | ❌ | ❌ | ✅ | ❌ |
|          `⫓̸` | ❌ | ❌ | ❌ | ❌ |

"**But wait!!!** This seems really familiar," you might say. And you'd be correct! Let us rename:

* `⊂` => `0`
* `=` => `1`
* `⊃` => `Infinity`
* `⫓̸` => `NaN`

And voilà!

| Row ≤ column | `0` | `1` | `Infinity` | `NaN` |
| ------------:|:---:|:---:|:----------:|:-----:|
|          `0` | ✅ | ✅ | ✅ | ❌ |
|          `1` | ❌ | ✅ | ✅ | ❌ |
|   `Infinity` | ❌ | ❌ | ✅ | ❌ |
|        `NaN` | ❌ | ❌ | ❌ | ❌ |

SetVer defines a **comparison function**, which is a function that takes as input two version sets denoted by `X` and `Y`, and returns a floating-point number representing the result of the comparison. The output value is either `0`, `1`, `Infinity`, or `NaN`, denoting `X ⊂ Y`, `X = Y`, `X ⊃ Y` and `X ⫓̸ Y` respectively.

> For the technically inclined, this subset of floating-point numbers can be represented using a 1-bit significand and 1-bit mantissa, omitting the sign bit entirely. Unfortunately, binary2 is not a valid floating point format according to IEEE 754. As I cry myself to sleep, I rewatch [NaN Gates and Flip FLOPS](https://www.youtube.com/watch?v=5TFDG-y-EHs) for the seventh time.

## Alternative Representations

SetVer defines three alternative representations for version sets:

### The Integralternative

By replacing the `{` characters in a version set with `0` and `}` with `1`, one obtains a bitstring which can then be interpreted as an integer and efficiently transmitted over the network, stored in files or spoken out loud to your friends. (You should mention SetVer to your friends.)

As an example: `{{{{}}{}}{{}}}` => `00001101100111` => `871`

As version sets are unordered, we also have: `{{{}}{{{}}{}}}` => `00011000110111` => `1591`

Therefore, under the Integralternative scheme, `871 = 1591`.

### The Picturrogate

A version set can be encoded in a picture, such that every pair of braces `{}` is represented by a closed loop, and every element inside braces is drawn inside the loop.

As an example: `{{{{}}{}}{{}}}` =>

![Visual representation of the version set with loops](https://raw.githubusercontent.com/RocketRace/setver/main/%7B%7B%7B%7D%7D%7B%7D%7D.png)

### The SubstiTODO

# So, uh, that's a lot of prose... What does this mean in practice?

Rejoice! Evangelize! Be annoying to everyone you know and spread the word of SetVer! We have all been blessed by the light of the bristly-looking set braces.

SetVer is wonderfully intuitive, elegant, and versatile. It's so perfect in fact that it can even represent concepts such as branches in version control natively, using the fact that some sets compare as `NaN`. (I told you I would come back to this later!)

So here's an example of a project using SetVer efficiently (the time axis is downwards)!

```
            {}  <- initial version
            |
            v
           {{}}  <- some changes
            |
      /-----+------\  <- diverging history
      |            |
      v            |
   {{}{{}}}        |  <- changes in the first branch
      |            v
      |        {{}{{{}}}}  <- changes in the second branch
      v            |
{{}{{}}{{}{{}}}}   |  <- more changes in the first branch
      |            |
      +------------/  <- branches merge back
      |
      v
{{}{{}}{{}{{}}}{{{}}}}  <- version containing all previous changes
```

That's right, Git should shake in its boots because SetVer could soon replace all of version control! (That's on the roadmap, actually. "Replace all of version control.")

## Implementations (the apostles)

* [`setver`](https://crates.io/crates/setver), a 🦀 Rust 🦀 crate by [kleines Filmröllchen](https://github.com/kleinesfilmroellchen) — Unfortunately, crates.io has not yet acquiesced the revolutionary nature of SetVer and instead oppressively forces SemVer for its crates 😔

--------

SetVer is licensed under CC0. Use it however you want.

repo_name: RocketRace/setver


text:
Written by Lightbend <info@lightbend.com>

To the extent possible under law, the author(s) have dedicated all copyright and
related and neighboring rights to this software to the public domain worldwide.
This software is distributed without any warranty.

You should have received a copy of the CC0 Public Domain Dedication along with
this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.

repo_language: Scala


text:
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem  Gradle startup script for Windows
@rem
@rem ##########################################################################

@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal

set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%

@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=

@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome

set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init

echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe

if exist "%JAVA_EXE%" goto init

echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:init
@rem Get command-line arguments, handling Windows variants

if not "%OS%" == "Windows_NT" goto win9xME_args

:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2

:win9xME_args_slurp
if "x%~1" == "x" goto execute

set CMD_LINE_ARGS=%*

:execute
@rem Setup the command line

set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd

:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1

:mainEnd
if "%OS%"=="Windows_NT" endlocal

:omega


text:
import play.sbt.PlaySettings
import sbt.Keys._

lazy val GatlingTest = config("gatling") extend Test

scalaVersion in ThisBuild := "2.12.7"

libraryDependencies += guice
libraryDependencies += "org.joda" % "joda-convert" % "2.1.2"
libraryDependencies += "net.logstash.logback" % "logstash-logback-encoder" % "5.2"

libraryDependencies += "com.netaporter" %% "scala-uri" % "0.4.16"
libraryDependencies += "net.codingwell" %% "scala-guice" % "4.2.1"

libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "4.0.1" % Test
libraryDependencies += "io.gatling.highcharts" % "gatling-charts-highcharts" % "3.0.1.1" % Test
libraryDependencies += "io.gatling" % "gatling-test-framework" % "3.0.1.1" % Test

// The Play project itself
lazy val root = (project in file("."))
  .enablePlugins(Common, PlayService, PlayLayoutPlugin, GatlingPlugin)
  .configs(GatlingTest)
  .settings(inConfig(GatlingTest)(Defaults.testSettings): _*)
  .settings(
    name := """play-scala-rest-api-example""",
    scalaSource in GatlingTest := baseDirectory.value / "/gatling/simulation"
  )

// Documentation for this project:
//    sbt "project docs" "~ paradox"
//    open docs/target/paradox/site/index.html
lazy val docs = (project in file("docs")).enablePlugins(ParadoxPlugin).
  settings(
    paradoxProperties += ("download_url" -> "https://example.lightbend.com/v1/download/play-rest-api")
  )


text:
#!/usr/bin/env sh

##############################################################################
##
##  Gradle start up script for UN*X
##
##############################################################################

# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
    ls=`ls -ld "$PRG"`
    link=`expr "$ls" : '.*-> \(.*\)$'`
    if expr "$link" : '/.*' > /dev/null; then
        PRG="$link"
    else
        PRG=`dirname "$PRG"`"/$link"
    fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null

APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`

# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"

warn () {
    echo "$*"
}

die () {
    echo
    echo "$*"
    echo
    exit 1
}

# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
  CYGWIN* )
    cygwin=true
    ;;
  Darwin* )
    darwin=true
    ;;
  MINGW* )
    msys=true
    ;;
  NONSTOP* )
    nonstop=true
    ;;
esac

CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar

# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
        # IBM's JDK on AIX uses strange locations for the executables
        JAVACMD="$JAVA_HOME/jre/sh/java"
    else
        JAVACMD="$JAVA_HOME/bin/java"
    fi
    if [ ! -x "$JAVACMD" ] ; then
        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
    fi
else
    JAVACMD="java"
    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi

# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
    MAX_FD_LIMIT=`ulimit -H -n`
    if [ $? -eq 0 ] ; then
        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
            MAX_FD="$MAX_FD_LIMIT"
        fi
        ulimit -n $MAX_FD
        if [ $? -ne 0 ] ; then
            warn "Could not set maximum file descriptor limit: $MAX_FD"
        fi
    else
        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
    fi
fi

# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi

# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
    JAVACMD=`cygpath --unix "$JAVACMD"`

    # We build the pattern for arguments to be converted via cygpath
    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
    SEP=""
    for dir in $ROOTDIRSRAW ; do
        ROOTDIRS="$ROOTDIRS$SEP$dir"
        SEP="|"
    done
    OURCYGPATTERN="(^($ROOTDIRS))"
    # Add a user-defined pattern to the cygpath arguments
    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
    fi
    # Now convert the arguments - kludge to limit ourselves to /bin/sh
    i=0
    for arg in "$@" ; do
        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option

        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
        else
            eval `echo args$i`="\"$arg\""
        fi
        i=$((i+1))
    done
    case $i in
        (0) set -- ;;
        (1) set -- "$args0" ;;
        (2) set -- "$args0" "$args1" ;;
        (3) set -- "$args0" "$args1" "$args2" ;;
        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
    esac
fi

# Escape application args
save () {
    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
    echo " "
}
APP_ARGS=$(save "$@")

# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"

# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
  cd "$(dirname "$0")"
fi

exec "$JAVACMD" "$@"


text: MOVED TO https://github.com/playframework/play-samples


text:
// You will need private bintray credentials to publish this with Lightbend theme
// credentials += Credentials("Bintray", "dl.bintray.com", "<user>", "<bintray API key>")
// resolvers += "bintray-typesafe-internal-maven-releases" at "https://dl.bintray.com/typesafe/internal-maven-releases/"
// paradoxTheme := Some("com.lightbend.paradox" % "paradox-theme-lightbend" % "0.2.3")

// Uses the out of the box generic theme.
paradoxTheme := Some(builtinParadoxTheme("generic"))


text:
# Making a REST API with Play

This is a multi-part guide to walk you through how to make a RESTful API with JSON using [Play Framework](https://playframework.com).

We’ll demonstrate with a "best practices" REST API.  You can get source code for this guide two ways:

## From Lightbend Tech Hub

Download a pre-packaged bundle with this link [https://example.lightbend.com/v1/download/play-scala-rest-api-example](https://example.lightbend.com/v1/download/play-scala-rest-api-example)

**Linux/Mac:**

```bash
unzip play-scala-rest-api-example.zip
cd play-scala-rest-api-example
./sbt
```

**Windows:**

1. Unzip the download
1. From a command line `cd` into the directory where you expanded the downloaded `zip` file and run:

```bash
sbt.bat
```

## [From Github](https://github.com/playframework/play-scala-rest-api-example/tree/2.6.x):

```bash
git clone https://github.com/playframework/play-scala-rest-api-example.git
git checkout 2.6.x
```

This example is in Scala, but Play also has a [Java API](https://www.playframework.com/documentation/latest/JavaHome) which looks and acts just like the [Scala API](https://www.playframework.com/documentation/latest/ScalaHome), and has a corresponding [play-java-rest-api-example](https://github.com/playframework/play-java-rest-api-example) project.  For instructions on running and using the project, please see the [[appendix]].  This project also comes with an integrated [Gatling](http://gatling.io/) load test -- again, instructions are in the appendix.

Note that there’s more involved in a REST API -- monitoring, representation, and managing access to back end resources -- that we'll cover in subsequent posts.  But first, let's address why Play is so effective as a REST API.

## When to use Play

Play makes a good REST API implementation because Play does the right thing out of the box.  Play makes simple things easy, makes hard things possible, and encourages code that scales because it works in sympathy with the JVM and the underlying hardware. But "safe and does the right thing" is the boring answer.

The fun answer is that [Play is **fast**](https://www.lightbend.com/blog/why-is-play-framework-so-fast).

In fact, Play is so fast that you have to turn off machines so that the rest of your architecture can keep up.  The Hootsuite team was able to **reduce the number of servers by 80%** by [switching to Play](https://www.lightbend.com/resources/case-studies-and-stories/how-hootsuite-modernized-its-url-shortener).  if you deploy Play with the same infrastructure that you were using for other web frameworks, you are effectively staging a denial of service attack against your own database.

Play is fast because Play is **built on reactive bedrock**.  Play starts from a reactive core, and builds on reactive principles all the way from the ground.  Play breaks network packets into a stream of small chunks of bytes.  It keeps a small pool of work stealing threads, mapped to the number of cores in the machine, and keeps those threads fed with those chunks.  Play exposes those byte chunks to the application for body parsing, Server Sent Events and WebSockets through [Akka Streams](http://doc.akka.io/docs/akka/2.4/scala/stream/stream-introduction.html) -- the Reactive Streams implementation designed by the people who invented [Reactive Streams](http://www.reactive-streams.org/) and wrote the [Reactive Manifesto](http://www.reactivemanifesto.org/).

Linkedin uses Play throughout its infrastructure. It wins on all [four quadrants of scalability](http://www.slideshare.net/brikis98/the-play-framework-at-linkedin/128-Outline1_Getting_started_with_Play2) ([video](https://youtu.be/8z3h4Uv9YbE)).  Play's average "request per second" comes in around [tens of k on a basic quad core w/o any intentional tuning](https://twitter.com/kevinbowling1/status/764188720140398592) -- and it only gets better.

Play provides an easy to use MVC paradigm, including hot-reloading without any JVM bytecode magic or container overhead.  Startup time for a developer on Play was **reduced by roughly 7 times** for [Walmart Canada](https://www.lightbend.com/resources/case-studies-and-stories/walmart-boosts-conversions-by-20-with-lightbend-reactive-platform), and using Play **reduced development times by 2x to 3x**.

Play combines this with a **reactive programming API** that lets you write async, non-blocking code in a straightforward fashion without worrying about complex and confusing "callback hell."  In both Java or Scala, Play works on the same principle: leverage the asynchronous computation API that the language provides to you.  In Play, you work with [`java.util.concurrent.CompletionStage`](https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/changes8.html) or [`scala.concurrent.Future`](http://docs.scala-lang.org/overviews/core/futures.html) API directly, and Play passes that asynchronous computation back through the framework.

Finally, Play is modular and extensible.  Play works with multiple runtime and compile time dependency injection frameworks like [Guice](https://www.playframework.com/documentation/latest/ScalaDependencyInjection), [Macwire](https://di-in-scala.github.io/), [Dagger](https://github.com/playframework/play-java-dagger2-example), and leverages DI principles to integrate authentication and authorization frameworks built on top of Play.

## Community

To learn more about Play, check out the [Play tutorials](https://playframework.com/documentation/latest/Tutorials) and see more examples and blog posts about Play, including streaming [Server Side Events](https://github.com/playframework/play-streaming-scala) and first class [WebSocket support](https://github.com/playframework/play-websocket-scala).

To get more involved and if you have questions, join the [forums](https://discuss.playframework.com) at  and follow [PlayFramework on Twitter](https://twitter.com/playframework).

## Microservices vs REST APIs

One thing to note here is that although this guide covers how to make a REST API in Play, it only covers Play itself and deploying Play.  Building a REST API in Play does not automatically make it a "microservice" because it does not cover larger scale concerns about microservices such as ensuring resiliency, consistency, or monitoring.

For full scale microservices, you want [Lagom](http://www.lagomframework.com/), which builds on top of Play -- a microservices framework for dealing with the ["data on the outside"](https://blog.acolyer.org/2016/09/13/data-on-the-outside-versus-data-on-the-inside/) problem, set up with persistence and service APIs that ensure that the service always stays up and responsive even in the face of chaos monkeys and network partitions.

With that caveat, let's @ref[start working with Play](part-1/index.md)!

@@@index

* [Basics](part-1/index.md)
* [Appendix](appendix.md)

@@@


text:
# Appendix

This appendix covers how to download, run, use and load test Play.

## Requirements

You will need a JDK 1.8 that is more recent than b20.  You can download the JDK from [here](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html).

You will need to have [git](https://git-scm.com/) installed.

## Downloading

You can clone the example project from Github:

```bash
git clone https://github.com/playframework/play-scala-rest-api-example.git
```

## Running

You need to download and install sbt for this application to run.  You can do that by going to the [sbt download page](http://www.scala-sbt.org/download.html) and following the instructions for your platform.

Once you have sbt installed, the following at the command prompt will download any required library dependencies, and start up Play in development mode:

```bash
sbt run
```

Play will start up on the HTTP port at <http://localhost:9000/>.   You don't need to deploy or reload anything -- changing any source code while the server is running will automatically recompile and hot-reload the application on the next HTTP request.  You can read more about using Play [here](https://www.playframework.com/documentation/latest/PlayConsole).

## Usage

If you call the same URL from the command line, you’ll see JSON. Using [httpie](https://httpie.org/), we can execute the command:

```bash
http --verbose http://localhost:9000/v1/posts
```

And get back:

```
GET /v1/posts HTTP/1.1
```

Likewise, you can also send a POST directly as JSON:

```bash
http --verbose POST http://localhost:9000/v1/posts title="hello" body="world"
```

and get:

```
POST /v1/posts HTTP/1.1
```

## Load Testing

The best way to see what Play can do is to run a load test.  We've included [Gatling](https://gatling.io/) in this test project for integrated load testing.

Start Play in production mode, by [staging the application](https://www.playframework.com/documentation/latest/Deploying) and running the play scripts:

```bash
sbt stage
./target/universal/stage/bin/play-scala-rest-api-example -Dplay.http.secret.key=testing
```

Then you'll start the Gatling load test up (it's already integrated into the project):

```bash
sbt gatling:test
```

For best results, start the gatling load test up on another machine so you do not have contending resources.  You can edit the [Gatling simulation](http://gatling.io/docs/2.2.2/general/simulation_structure.html#simulation-structure), and change the numbers as appropriate.

Once the test completes, you'll see an HTML file containing the load test chart:

```bash
./rest-api/target/gatling/gatlingspec-1472579540405/index.html
```

That will contain your load test results.


text:
# Basics

This guide will walk you through how to make a REST API with JSON using [Play Framework](https://playframework.com).

To see the associated Github project, please go to <https://github.com/playframework/play-scala-rest-api-example> or clone the project:

```bash
git clone https://github.com/playframework/play-scala-rest-api-example.git
```

We're going to be showing an already working Play project with most of the code available under the `app/v1` directory.  There will be several different versions of the same project as this series expands, so you can compare different versions of the project against each other.

To run Play on your own local computer, please see the instructions in the @ref[appendix](../appendix.md).

## Introduction

We'll start off with a REST API that displays information for blog posts.  Users should be able to write a title and a body of a blog post and create new blog posts, edit existing blog posts, and delete new blog posts.

## Modelling a Post Resource

The way to do this in REST is to model the represented state as a resource.  A blog post resource will have a unique id, a URL hyperlink that indicates the canonical location of the resource, the title of the blog post, and the body of the blog post.

This resource is represented as a single case class in the Play application [here](https://github.com/playframework/play-scala-rest-api-example/blob/2.6.x/app/v1/post/PostResourceHandler.scala#L13):

```scala
case class PostResource(
  id: String,
  link: String,
  title: String,
  body: String
)
```

This resource is mapped to and from JSON on the front end using Play, and is mapped to and from a persistent datastore on the backend using a handler.

Play handles HTTP routing and representation for the REST API and makes it easy to write a non-blocking, asynchronous API that is an order of magnitude more efficient than other web application frameworks.

## Routing Post Requests

Play has two complimentary routing mechanisms.  In the conf directory, there's a file called "routes" which contains entries for the HTTP method and a relative URL path, and points it at an action in a controller.

```
GET    /               controllers.HomeController.index()
```

This is useful for situations where a front end service is rendering HTML.  However, Play also contains a more powerful routing DSL that we will use for the REST API.

For every HTTP request starting with `/v1/posts`, Play routes it to a dedicated `PostRouter` class to handle the Posts resource, through the [`conf/routes`](https://github.com/playframework/play-scala-rest-api-example/blob/2.6.x/conf/routes) file:

```
->     /v1/posts               v1.post.PostRouter
```

The `PostRouter` examines the URL and extracts data to pass along to the controller [here](https://github.com/playframework/play-scala-rest-api-example/blob/2.6.x/app/v1/post/PostRouter.scala):

```scala
package v1.post

import javax.inject.Inject

import play.api.routing.Router.Routes
import play.api.routing.SimpleRouter
import play.api.routing.sird._

class PostRouter @Inject()(controller: PostController) extends SimpleRouter {
  val prefix = "/v1/posts"

  def link(id: PostId): String = {
    import com.netaporter.uri.dsl._
    val url = prefix / id.toString
    url.toString()
  }

  override def routes: Routes = {
    case GET(p"/") =>
      controller.index

    case POST(p"/") =>
      controller.process

    case GET(p"/$id") =>
      controller.show(id)
  }

}
```

Play’s [routing DSL](https://www.playframework.com/documentation/latest/ScalaSirdRouter) (technically "String Interpolation Routing DSL", aka SIRD) shows how data can be extracted from the URL concisely and cleanly.  SIRD is based around HTTP methods and a string interpolated extractor object – this means that when we type the string “/$id” and prefix it with “p”, then the path parameter id can be extracted and used in the block. Naturally, there are also operators to extract queries, regular expressions, and even add custom extractors.  If you have a URL as follows:

```
/posts/?sort=ascending&count=5
```

Then you can extract the "sort" and "count" parameters in a single line:

```scala
GET("/" ? q_?"sort=$sort" & q_?”count=${ int(count) }")
```

SIRD is especially useful in a REST API where there can be many possible query parameters. Cake Solutions covers SIRD in more depth in a [fantastic blog post](http://www.cakesolutions.net/teamblogs/all-you-need-to-know-about-plays-routing-dsl).

## Using a Controller

The `PostRouter` has a `PostController` injected into it through standard [JSR-330 dependency injection](https://github.com/google/guice/wiki/JSR330) [here](https://github.com/playframework/play-scala-rest-api-example/blob/2.6.x/app/v1/post/PostRouter.scala#L12):

```scala
class PostRouter @Inject()(controller: PostController) extends SimpleRouter
```

Before heading into the `PostController`, let's discuss how controllers work in Play.

A controller [handles the work of processing](https://www.playframework.com/documentation/latest/ScalaActions)  the HTTP request into an HTTP response in the context of an Action: it's where page rendering and HTML form processing happen.  A controller extends [`play.api.mvc.BaseController`](https://www.playframework.com/documentation/latest/api/scala/index.html#play.api.mvc.BaseController), which contains a number of utility methods and constants for working with HTTP.  In particular, a `Controller` contains `Result` objects such as `Ok` and `Redirect`, and `HeaderNames` like `ACCEPT`.

The methods in a controller consist of a method returning an [Action](https://www.playframework.com/documentation/latest/api/scala/index.html#play.api.mvc.Action).  The Action provides the "engine" to Play.

Using the action, the controller passes in a block of code that takes a [`Request`](https://www.playframework.com/documentation/latest/api/scala/index.html#play.api.mvc.Request) passed in as implicit – this means that any in-scope method that takes an implicit request as a parameter will use this request automatically.  Then, the block must return either a [`Result`](https://www.playframework.com/documentation/latest/api/scala/index.html#play.api.mvc.Result), or a [`Future[Result]`](http://www.scala-lang.org/api/current/index.html#scala.concurrent.Future), depending on whether or not the action was called as `action { ... }` or [`action.async { ... }`](https://www.playframework.com/documentation/latest/ScalaAsync#How-to-create-a-Future[Result]).

### Handling GET Requests

Here's a simple example of a Controller:

```scala
import javax.inject.Inject
import play.api.mvc._

import scala.concurrent._

class MyController @Inject()(val controllerComponents: ControllerComponents) extends BaseController {

  def index1: Action[AnyContent] = Action { implicit request =>
    val r: Result = Ok("hello world")
    r
  }

  def asyncIndex: Action[AnyContent] = Action.async { implicit request =>
    val r: Future[Result] = Future.successful(Ok("hello world"))
    r
  }
}
```

In this example, `index1` and `asyncIndex` have exactly the same behavior.  Internally, it makes no difference whether we call `Result` or `Future[Result]` -- Play is non-blocking all the way through.

However, if you're already working with `Future`, async makes it easier to pass that `Future` around. You can read more about this in the [handling asynchronous results](https://www.playframework.com/documentation/latest/ScalaAsync) section of the Play documentation.

The PostController methods dealing with GET requests is [here](https://github.com/playframework/play-scala-rest-api-example/blob/2.6.x/app/v1/post/PostController.scala). Let's take a look at the most important parts:

```scala
package v1.post

import javax.inject.Inject

import play.api.Logger
import play.api.data.Form
import play.api.libs.json.Json
import play.api.mvc._

import scala.concurrent.{ ExecutionContext, Future }

class PostController @Inject()(cc: PostControllerComponents)(implicit ec: ExecutionContext)
  extends PostBaseController(cc) {

  def index: Action[AnyContent] = PostAction.async { implicit request =>
    logger.trace("index: ")
    postResourceHandler.find.map { posts =>
      Ok(Json.toJson(posts))
    }
  }

  def show(id: String): Action[AnyContent] = PostAction.async { implicit request =>
    logger.trace(s"show: id = $id")
    postResourceHandler.lookup(id).map { post =>
      Ok(Json.toJson(post))
    }
  }
}
```

Let's take `show` as an example.  Here, the action defines a workflow for a request that maps to a single resource, i.e. `GET /v1/posts/123`.

```scala
def show(id: String): Action[AnyContent] = PostAction.async { implicit request =>
  logger.trace(s"show: id = $id")
  postResourceHandler.lookup(id).map { post =>
    Ok(Json.toJson(post))
  }
}
```

The `id` is passed in as a `String`, and the handler looks up and returns a `PostResource`.  The `Ok()` sends back a `Result` with a status code of "200 OK", containing a response body consisting of the `PostResource` serialized as JSON.

### Processing Form Input

Handling a `POST` request is also easy and is done through the `process` method:

```scala
private val form: Form[PostFormInput] = {
  import play.api.data.Forms._

  Form(
    mapping(
      "title" -> nonEmptyText,
      "body" -> text
    )(PostFormInput.apply)(PostFormInput.unapply)
  )
}

def process: Action[AnyContent] = PostAction.async { implicit request =>
  logger.trace("process: ")
  processJsonPost()
}

private def processJsonPost[A]()(implicit request: PostRequest[A]): Future[Result] = {
  def failure(badForm: Form[PostFormInput]) = {
    Future.successful(BadRequest(badForm.errorsAsJson))
  }

  def success(input: PostFormInput) = {
    postResourceHandler.create(input).map { post =>
      Created(Json.toJson(post)).withHeaders(LOCATION -> post.link)
    }
  }

  form.bindFromRequest().fold(failure, success)
}
```

Here, the `process` action is an action wrapper, and `processJsonPost` does most of the work.  In `processJsonPost`, we get to the [form processing](https://www.playframework.com/documentation/latest/ScalaForms) part of the code.

Here, `form.bindFromRequest()` will map input from the HTTP request to a [`play.api.data.Form`](https://www.playframework.com/documentation/latest/api/scala/index.html#play.api.data.Form), and handles form validation and error reporting.

If the `PostFormInput` passes validation, it's passed to the resource handler, using the `success` method.  If the form processing fails, then the `failure` method is called and the `FormError` is returned in JSON format.

```scala
private val form: Form[PostFormInput] = {
  import play.api.data.Forms._

  Form(
    mapping(
      "title" -> nonEmptyText,
      "body" -> text
    )(PostFormInput.apply)(PostFormInput.unapply)
  )
}
```

The form binds to the HTTP request using the names in the mapping -- `title` and `body` to the `PostFormInput` case class [here](https://github.com/playframework/play-scala-rest-api-example/blob/2.6.x/app/v1/post/PostController.scala#L12).

```scala
case class PostFormInput(title: String, body: String)
```

That's all you need to do to handle a basic web application!  As with most things, there are more details that need to be handled.  That's where creating custom Actions comes in.

## Using Actions

We saw in the `PostController` that each method is connected to an Action through the `PostAction.async` method [here](https://github.com/playframework/play-scala-rest-api-example/blob/2.6.x/app/v1/post/PostController.scala#L33):

```scala
def index: Action[AnyContent] = PostAction.async { implicit request =>
  logger.trace("index: ")
  postResourceHandler.find.map { posts =>
    Ok(Json.toJson(posts))
  }
}
```

The `PostAction.async` is a [custom action builder](https://www.playframework.com/documentation/2.6.x/ScalaActionsComposition#Custom-action-builders) defined [here](https://github.com/playframework/play-scala-rest-api-example/blob/2.6.x/app/v1/post/PostActionBuilder.scala#L49-L53) that can handle `PostRequest`s (see definition [here](https://github.com/playframework/play-scala-rest-api-example/blob/2.6.x/app/v1/post/PostActionBuilder.scala#L20)):

`PostAction` is involved in each action in the controller -- it mediates the paperwork involved with processing a request into a response, adding context to the request and enriching the response with headers and cookies.  ActionBuilders are essential for handling authentication, authorization and monitoring functionality.

ActionBuilders work through a process called [action composition](https://www.playframework.com/documentation/latest/ScalaActionsComposition).  The ActionBuilder class has a method called `invokeBlock` that takes in a `Request` and a function (also known as a block, lambda or closure) that accepts a `Request` of a given type, and produces a `Future[Result]`.

So, if you want to work with an `Action` that has a "FooRequest" that has a Foo attached, it's easy:

```scala
class FooRequest[A](request: Request[A], val foo: Foo) extends WrappedRequest(request)

class FooAction @Inject()(parsers: PlayBodyParsers)(implicit val executionContext: ExecutionContext) extends ActionBuilder[FooRequest, AnyContent] {

  type FooRequestBlock[A] = FooRequest[A] => Future[Result]

  override def parser: BodyParser[AnyContent] = parsers.defaultBodyParser

  override def invokeBlock[A](request: Request[A], block: FooRequestBlock[A]): Future[Result] = {
    block(new FooRequest[A](request, Foo()))
  }
}
```

You create an `ActionBuilder[FooRequest, AnyContent]`, override `invokeBlock`, and then call the function with an instance of `FooRequest`.

Then, when you call `fooAction`, the request type is `FooRequest`:

```scala
fooAction { request: FooRequest =>
  Ok(request.foo.toString)
}
```

And `request.foo` will be added automatically.

You can keep composing action builders inside each other, so you don't have to layer all the functionality in one single ActionBuilder, or you can create a custom `ActionBuilder` for each package you work with, according to your taste.  For the purposes of this blog post, we'll keep everything together in a single class.

You can see `PostAction` builder [here](https://github.com/playframework/play-scala-rest-api-example/blob/2.6.x/app/v1/post/PostActionBuilder.scala#L49-L78):

```scala
trait PostRequestHeader extends MessagesRequestHeader with PreferredMessagesProvider
class PostRequest[A](request: Request[A], val messagesApi: MessagesApi) extends WrappedRequest(request) with PostRequestHeader

class PostActionBuilder @Inject()(messagesApi: MessagesApi, playBodyParsers: PlayBodyParsers)
                                 (implicit val executionContext: ExecutionContext)
    extends ActionBuilder[PostRequest, AnyContent]
    with RequestMarkerContext
    with HttpVerbs {

  val parser: BodyParser[AnyContent] = playBodyParsers.anyContent

  type PostRequestBlock[A] = PostRequest[A] => Future[Result]

  private val logger = Logger(this.getClass)

  override def invokeBlock[A](request: Request[A],
                              block: PostRequestBlock[A]): Future[Result] = {
    // Convert to marker context and use request in block
    implicit val markerContext: MarkerContext = requestHeaderToMarkerContext(request)
    logger.trace(s"invokeBlock: ")

    val future = block(new PostRequest(request, messagesApi))

    future.map { result =>
      request.method match {
        case GET | HEAD =>
          result.withHeaders("Cache-Control" -> s"max-age: 100")
        case other =>
          result
      }
    }
  }
}
```

`PostAction` does a couple of different things here.  The first thing it does is to log the request as it comes in.  Next, it pulls out `MessagesApi` for the request, and adds that to a `PostRequest` , and runs the function, returning a `Future[Result]`.

When the future completes, we map the result so we can replace it with a slightly different result.  We compare the result's method against `HttpVerbs`, and if it's a GET or HEAD, we append a `Cache-Control` header with a `max-age` directive.  We need an `ExecutionContext` for `future.map` operations, so we pass in the default execution context implicitly at the top of the class.

Now that we have a `PostRequest`, we can call "request.messagesApi" explicitly from any action in the controller, for free, and we can append information to the result after the user action has been completed.

## Converting resources with PostResourceHandler

The `PostResourceHandler` is responsible for converting backend data from a repository into a `PostResource`. We won't go into detail on the `PostRepository` details for now, only that it returns data in an backend-centric state.

A REST resource has information that a backend repository does not -- it knows about the operations available on the resource, and contains URI information that a single backend may not have.  As such, we want to be able to change the representation that we use internally without changing the resource that we expose publicly.

You can see the `PostResourceHandler` [here](https://github.com/playframework/play-scala-rest-api-example/blob/2.6.x/app/v1/post/PostResourceHandler.scala#L35-L66):

```scala
class PostResourceHandler @Inject()(
    routerProvider: Provider[PostRouter],
    postRepository: PostRepository)(implicit ec: ExecutionContext) {

  def create(postInput: PostFormInput)(implicit mc: MarkerContext): Future[PostResource] = {
    val data = PostData(PostId("999"), postInput.title, postInput.body)
    // We don't actually create the post, so return what we have
    postRepository.create(data).map { id =>
      createPostResource(data)
    }
  }

  def lookup(id: String)(implicit mc: MarkerContext): Future[Option[PostResource]] = {
    val postFuture = postRepository.get(PostId(id))
    postFuture.map { maybePostData =>
      maybePostData.map { postData =>
        createPostResource(postData)
      }
    }
  }

  def find(implicit mc: MarkerContext): Future[Iterable[PostResource]] = {
    postRepository.list().map { postDataList =>
      postDataList.map(postData => createPostResource(postData))
    }
  }

  private def createPostResource(p: PostData): PostResource = {
    PostResource(p.id.toString, routerProvider.get.link(p.id), p.title, p.body)
  }

}
```
Here, it's a straight conversion in `createPostResource`, with the only hook being that the router provides the resource's URL, since it's something that `PostData` does not have itself.

## Rendering Content as JSON

Play handles the work of converting a `PostResource` through [Play JSON](https://www.playframework.com/documentation/latest/ScalaJson). Play JSON provides a DSL that looks up the conversion for the `PostResource` singleton object, so you don't need to declare it at the use point.

You can see the `PostResource` object [here](https://github.com/playframework/play-scala-rest-api-example/blob/2.6.x/app/v1/post/PostResourceHandler.scala#L15-L30):

```scala
object PostResource {

  implicit val implicitWrites = new Writes[PostResource] {
    def writes(post: PostResource): JsValue = {
      Json.obj(
        "id" -> post.id,
        "link" -> post.link,
        "title" -> post.title,
        "body" -> post.body
      )
    }
  }
}
```

Once the implicit is defined in the companion object, then it will be looked up automatically when handed an instance of the class.  This means that when the controller converts to JSON, the conversion will just work, without any additional imports or setup.

```scala
val json: JsValue = Json.toJson(post)
```

Play JSON also has options to incrementally parse and generate JSON for continuously streaming JSON responses.

## Summary

We've shown how to easy it is to put together a basic REST API in Play.  Using this code, we can put together backend data, convert it to JSON and transfer it over HTTP with a minimum of fuss.

In the next guide, we'll discuss content representation and provide an HTML interface that exists alongside the JSON API.


text:
# These settings are synced to GitHub by https://probot.github.io/apps/settings/
repository:
  homepage: "https://developer.lightbend.com/start/?group=play"
  topics: playframework, example, example-project, sample, sample-app, jvm, webapp
  private: false
  has_issues: true
  # We don't need projects in sample projects
  has_projects: false
  # We don't need wiki in sample projects
  has_wiki: false
  has_downloads: true
  default_branch: 2.7.x
  allow_squash_merge: true
  allow_merge_commit: false
  allow_rebase_merge: false

teams:
  - name: core
    permission: admin
  - name: integrators
    permission: write
  - name: write-bots
    permission: write

branches:
  - name: "[0-9].*.x"
    protection:
      # We don't require reviews for sample applications because they are mainly
      # updated by template-control, which is an automated process
      required_pull_request_reviews: null
      # Required. Require status checks to pass before merging. Set to null to disable
      required_status_checks:
        # Required. The list of status checks to require in order to merge into this branch
        contexts: ["Travis CI - Pull Request", "typesafe-cla-validator"]

# Labels: tailored list of labels to be used by sample applications
labels:
  - color: f9d0c4
    name: "closed:declined"
  - color: f9d0c4
    name: "closed:duplicated"
    oldname: duplicate
  - color: f9d0c4
    name: "closed:invalid"
    oldname: invalid
  - color: f9d0c4
    name: "closed:question"
    oldname: question
  - color: f9d0c4
    name: "closed:wontfix"
    oldname: wontfix
  - color: 7057ff
    name: "good first issue"
  - color: 7057ff
    name: "Hacktoberfest"
  - color: 7057ff
    name: "help wanted"
  - color: cceecc
    name: "status:backlog"
    oldname: backlog
  - color: b60205
    name: "status:block-merge"
    oldname: block-merge
  - color: b60205
    name: "status:blocked"
  - color: 0e8a16
    name: "status:in-progress"
  - color: 0e8a16
    name: "status:merge-when-green"
    oldname: merge-when-green
  - color: fbca04
    name: "status:needs-backport"
  - color: fbca04
    name: "status:needs-forwardport"
  - color: fbca04
    name: "status:needs-info"
  - color: fbca04
    name: "status:needs-verification"
  - color: 0e8a16
    name: "status:ready"
  - color: fbca04
    name: "status:to-review"
    oldname: review
  - color: c5def5
    name: "topic:build/tests"
  - color: c5def5
    name: "topic:dev-environment"
  - color: c5def5
    name: "topic:documentation"
  - color: c5def5
    name: "topic:jdk-next"
  - color: b60205
    name: "type:defect"
    oldname: bug
  - color: 0052cc
    name: "type:feature"
  - color: 0052cc
    name: "type:improvement"
    oldname: enhancement
  - color: 0052cc
    name: "type:updates"
  - color: bf0d92
    name: "type:template-control"
    oldname: template-control


text:
import sbt.Keys._
import sbt._
import sbt.plugins.JvmPlugin

/**
  * Settings that are comment to all the SBT projects
  */
object Common extends AutoPlugin {
  override def trigger = allRequirements
  override def requires: sbt.Plugins = JvmPlugin

  override def projectSettings = Seq(
    organization := "com.lightbend.restapi",
    version := "1.0-SNAPSHOT",
    resolvers += Resolver.typesafeRepo("releases"),
    javacOptions ++= Seq("-source", "1.8", "-target", "1.8"),
    scalacOptions ++= Seq(
      "-encoding",
      "UTF-8", // yes, this is 2 args
      "-target:jvm-1.8",
      "-deprecation",
      "-feature",
      "-unchecked",
      "-Yno-adapted-args",
      "-Ywarn-numeric-widen",
      "-Xfatal-warnings"
    ),
    scalacOptions in Test ++= Seq("-Yrangepos"),
    autoAPIMappings := true
  )
}


text:
// The Play plugin
addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.7.0")

// sbt-paradox, used for documentation
addSbtPlugin("com.lightbend.paradox" % "sbt-paradox" % "0.4.4")

// Load testing tool:
// http://gatling.io/docs/2.2.2/extensions/sbt_plugin.html
addSbtPlugin("io.gatling" % "gatling-sbt" % "2.2.2")

// Scala formatting: "sbt scalafmt"
addSbtPlugin("com.lucidchart" % "sbt-scalafmt" % "1.15")


text:
#!/usr/bin/env bash

set -e
set -o pipefail

echo "+----------------------------+"
echo "| Compiling Gatling tests    |"
echo "+----------------------------+"
sbt ++$TRAVIS_SCALA_VERSION gatling:compile


text:
#!/usr/bin/env bash

set -e
set -o pipefail

# Using cut because TRAVIS_SCALA_VERSION is the full Scala
# version (for example 2.12.4), but Gradle expects just the
# binary version (for example 2.12)
scala_binary_version=$(echo $TRAVIS_SCALA_VERSION | cut -c1-4)

echo "+------------------------------+"
echo "| Executing tests using Gradle |"
echo "+------------------------------+"
./gradlew -Dscala.binary.version=$scala_binary_version check -i --stacktrace


text:
#!/usr/bin/env bash

set -e
set -o pipefail

echo "+----------------------------+"
echo "| Executing tests using sbt  |"
echo "+----------------------------+"
sbt ++$TRAVIS_SCALA_VERSION test


text:
import javax.inject._

import com.google.inject.AbstractModule
import net.codingwell.scalaguice.ScalaModule
import play.api.{Configuration, Environment}
import v1.post._

/**
  * Sets up custom components for Play.
  *
  * https://www.playframework.com/documentation/latest/ScalaDependencyInjection
  */
class Module(environment: Environment, configuration: Configuration)
    extends AbstractModule
    with ScalaModule {

  override def configure() = {
    bind[PostRepository].to[PostRepositoryImpl].in[Singleton]
  }
}


text:
import javax.inject.Inject
import play.api.OptionalDevContext
import play.api.http._
import play.api.mvc._
import play.api.mvc.request.RequestTarget
import play.api.routing.Router
import play.core.WebCommands

/**
  * Handles all requests.
  *
  * https://www.playframework.com/documentation/2.5.x/ScalaHttpRequestHandlers#extending-the-default-request-handler
  */
class RequestHandler @Inject()(webCommands: WebCommands,
                               optDevContext: OptionalDevContext,
                               router: Router,
                               errorHandler: HttpErrorHandler,
                               configuration: HttpConfiguration,
                               filters: HttpFilters)
    extends DefaultHttpRequestHandler(webCommands,
                                      optDevContext,
                                      router,
                                      errorHandler,
                                      configuration,
                                      filters) {

  override def handlerForRequest(
      request: RequestHeader): (RequestHeader, Handler) = {
    super.handlerForRequest {
      // ensures that REST API does not need a trailing "/"
      if (isREST(request)) {
        addTrailingSlash(request)
      } else {
        request
      }
    }
  }

  private def isREST(request: RequestHeader) = {
    request.uri match {
      case uri: String if uri.contains("post") => true
      case _                                   => false
    }
  }

  private def addTrailingSlash(origReq: RequestHeader): RequestHeader = {
    if (!origReq.path.endsWith("/")) {
      val path = origReq.path + "/"
      if (origReq.rawQueryString.isEmpty) {
        origReq.withTarget(
          RequestTarget(path = path, uriString = path, queryString = Map())
        )
      } else {
        origReq.withTarget(
          RequestTarget(path = path,
                        uriString = origReq.uri,
                        queryString = origReq.queryString)
        )
      }
    } else {
      origReq
    }
  }
}


text:
import javax.inject.{Inject, Provider}

import play.api._
import play.api.http.DefaultHttpErrorHandler
import play.api.http.Status._
import play.api.libs.json.Json
import play.api.mvc.Results._
import play.api.mvc._
import play.api.routing.Router
import play.core.SourceMapper

import scala.concurrent._

/**
  * Provides a stripped down error handler that does not use HTML in error pages, and
  * prints out debugging output.
  *
  * https://www.playframework.com/documentation/latest/ScalaErrorHandling
  */
class ErrorHandler(environment: Environment,
                   configuration: Configuration,
                   sourceMapper: Option[SourceMapper] = None,
                   optionRouter: => Option[Router] = None)
    extends DefaultHttpErrorHandler(environment,
                                    configuration,
                                    sourceMapper,
                                    optionRouter) {

  private val logger =
    org.slf4j.LoggerFactory.getLogger("application.ErrorHandler")

  // This maps through Guice so that the above constructor can call methods.
  @Inject
  def this(environment: Environment,
           configuration: Configuration,
           sourceMapper: OptionalSourceMapper,
           router: Provider[Router]) = {
    this(environment,
         configuration,
         sourceMapper.sourceMapper,
         Some(router.get))
  }

  override def onClientError(request: RequestHeader,
                             statusCode: Int,
                             message: String): Future[Result] = {
    logger.debug(
      s"onClientError: statusCode = $statusCode, uri = ${request.uri}, message = $message")

    Future.successful {
      val result = statusCode match {
        case BAD_REQUEST =>
          Results.BadRequest(message)
        case FORBIDDEN =>
          Results.Forbidden(message)
        case NOT_FOUND =>
          Results.NotFound(message)
        case clientError if statusCode >= 400 && statusCode < 500 =>
          Results.Status(statusCode)
        case nonClientError =>
          val msg =
            s"onClientError invoked with non client error status code $statusCode: $message"
          throw new IllegalArgumentException(msg)
      }
      result
    }
  }

  override protected def onDevServerError(
      request: RequestHeader,
      exception: UsefulException): Future[Result] = {
    Future.successful(
      InternalServerError(Json.obj("exception" -> exception.toString)))
  }

  override protected def onProdServerError(
      request: RequestHeader,
      exception: UsefulException): Future[Result] = {
    Future.successful(InternalServerError)
  }
}


text:
package v1.post

import javax.inject.Inject

import play.api.Logger
import play.api.data.Form
import play.api.libs.json.Json
import play.api.mvc._

import scala.concurrent.{ExecutionContext, Future}

case class PostFormInput(title: String, body: String)

/**
  * Takes HTTP requests and produces JSON.
  */
class PostController @Inject()(cc: PostControllerComponents)(
    implicit ec: ExecutionContext)
    extends PostBaseController(cc) {

  private val logger = Logger(getClass)

  private val form: Form[PostFormInput] = {
    import play.api.data.Forms._

    Form(
      mapping(
        "title" -> nonEmptyText,
        "body" -> text
      )(PostFormInput.apply)(PostFormInput.unapply)
    )
  }

  def index: Action[AnyContent] = PostAction.async { implicit request =>
    logger.trace("index: ")
    postResourceHandler.find.map { posts =>
      Ok(Json.toJson(posts))
    }
  }

  def process: Action[AnyContent] = PostAction.async { implicit request =>
    logger.trace("process: ")
    processJsonPost()
  }

  def show(id: String): Action[AnyContent] = PostAction.async {
    implicit request =>
      logger.trace(s"show: id = $id")
      postResourceHandler.lookup(id).map { post =>
        Ok(Json.toJson(post))
      }
  }

  private def processJsonPost[A]()(
      implicit request: PostRequest[A]): Future[Result] = {
    def failure(badForm: Form[PostFormInput]) = {
      Future.successful(BadRequest(badForm.errorsAsJson))
    }

    def success(input: PostFormInput) = {
      postResourceHandler.create(input).map { post =>
        Created(Json.toJson(post)).withHeaders(LOCATION -> post.link)
      }
    }

    form.bindFromRequest().fold(failure, success)
  }
}


text:
package v1.post

import javax.inject.Inject

import net.logstash.logback.marker.LogstashMarker
import play.api.{Logger, MarkerContext}
import play.api.http.{FileMimeTypes, HttpVerbs}
import play.api.i18n.{Langs, MessagesApi}
import play.api.mvc._

import scala.concurrent.{ExecutionContext, Future}

/**
  * A wrapped request for post resources.
  *
  * This is commonly used to hold request-specific information like
  * security credentials, and useful shortcut methods.
  */
trait PostRequestHeader
    extends MessagesRequestHeader
    with PreferredMessagesProvider
class PostRequest[A](request: Request[A], val messagesApi: MessagesApi)
    extends WrappedRequest(request)
    with PostRequestHeader

/**
  * Provides an implicit marker that will show the request in all logger statements.
  */
trait RequestMarkerContext {
  import net.logstash.logback.marker.Markers

  private def marker(tuple: (String, Any)) = Markers.append(tuple._1, tuple._2)

  private implicit class RichLogstashMarker(marker1: LogstashMarker) {
    def &&(marker2: LogstashMarker): LogstashMarker = marker1.and(marker2)
  }

  implicit def requestHeaderToMarkerContext(
      implicit request: RequestHeader): MarkerContext = {
    MarkerContext {
      marker("id" -> request.id) && marker("host" -> request.host) && marker(
        "remoteAddress" -> request.remoteAddress)
    }
  }

}

/**
  * The action builder for the Post resource.
  *
  * This is the place to put logging, metrics, to augment
  * the request with contextual data, and manipulate the
  * result.
  */
class PostActionBuilder @Inject()(messagesApi: MessagesApi,
                                  playBodyParsers: PlayBodyParsers)(
    implicit val executionContext: ExecutionContext)
    extends ActionBuilder[PostRequest, AnyContent]
    with RequestMarkerContext
    with HttpVerbs {

  override val parser: BodyParser[AnyContent] = playBodyParsers.anyContent

  type PostRequestBlock[A] = PostRequest[A] => Future[Result]

  private val logger = Logger(this.getClass)

  override def invokeBlock[A](request: Request[A],
                              block: PostRequestBlock[A]): Future[Result] = {
    // Convert to marker context and use request in block
    implicit val markerContext: MarkerContext = requestHeaderToMarkerContext(
      request)
    logger.trace(s"invokeBlock: ")

    val future = block(new PostRequest(request, messagesApi))

    future.map { result =>
      request.method match {
        case GET | HEAD =>
          result.withHeaders("Cache-Control" -> s"max-age: 100")
        case other =>
          result
      }
    }
  }
}

/**
  * Packages up the component dependencies for the post controller.
  *
  * This is a good way to minimize the surface area exposed to the controller, so the
  * controller only has to have one thing injected.
  */
case class PostControllerComponents @Inject()(
    postActionBuilder: PostActionBuilder,
    postResourceHandler: PostResourceHandler,
    actionBuilder: DefaultActionBuilder,
    parsers: PlayBodyParsers,
    messagesApi: MessagesApi,
    langs: Langs,
    fileMimeTypes: FileMimeTypes,
    executionContext: scala.concurrent.ExecutionContext)
    extends ControllerComponents

/**
  * Exposes actions and handler to the PostController by wiring the injected state into the base class.
  */
class PostBaseController @Inject()(pcc: PostControllerComponents)
    extends BaseController
    with RequestMarkerContext {
  override protected def controllerComponents: ControllerComponents = pcc

  def PostAction: PostActionBuilder = pcc.postActionBuilder

  def postResourceHandler: PostResourceHandler = pcc.postResourceHandler
}


text:
package v1.post

import javax.inject.{Inject, Provider}

import play.api.MarkerContext

import scala.concurrent.{ExecutionContext, Future}
import play.api.libs.json._

/**
  * DTO for displaying post information.
  */
case class PostResource(id: String, link: String, title: String, body: String)

object PostResource {
  /**
    * Mapping to read/write a PostResource out as a JSON value.
    */
    implicit val format: Format[PostResource] = Json.format
}

/**
  * Controls access to the backend data, returning [[PostResource]]
  */
class PostResourceHandler @Inject()(
    routerProvider: Provider[PostRouter],
    postRepository: PostRepository)(implicit ec: ExecutionContext) {

  def create(postInput: PostFormInput)(
      implicit mc: MarkerContext): Future[PostResource] = {
    val data = PostData(PostId("999"), postInput.title, postInput.body)
    // We don't actually create the post, so return what we have
    postRepository.create(data).map { id =>
      createPostResource(data)
    }
  }

  def lookup(id: String)(
      implicit mc: MarkerContext): Future[Option[PostResource]] = {
    val postFuture = postRepository.get(PostId(id))
    postFuture.map { maybePostData =>
      maybePostData.map { postData =>
        createPostResource(postData)
      }
    }
  }

  def find(implicit mc: MarkerContext): Future[Iterable[PostResource]] = {
    postRepository.list().map { postDataList =>
      postDataList.map(postData => createPostResource(postData))
    }
  }

  private def createPostResource(p: PostData): PostResource = {
    PostResource(p.id.toString, routerProvider.get.link(p.id), p.title, p.body)
  }

}


text:
package v1.post

import javax.inject.{Inject, Singleton}

import akka.actor.ActorSystem
import play.api.libs.concurrent.CustomExecutionContext
import play.api.{Logger, MarkerContext}

import scala.concurrent.Future

final case class PostData(id: PostId, title: String, body: String)

class PostId private (val underlying: Int) extends AnyVal {
  override def toString: String = underlying.toString
}

object PostId {
  def apply(raw: String): PostId = {
    require(raw != null)
    new PostId(Integer.parseInt(raw))
  }
}

class PostExecutionContext @Inject()(actorSystem: ActorSystem)
    extends CustomExecutionContext(actorSystem, "repository.dispatcher")

/**
  * A pure non-blocking interface for the PostRepository.
  */
trait PostRepository {
  def create(data: PostData)(implicit mc: MarkerContext): Future[PostId]

  def list()(implicit mc: MarkerContext): Future[Iterable[PostData]]

  def get(id: PostId)(implicit mc: MarkerContext): Future[Option[PostData]]
}

/**
  * A trivial implementation for the Post Repository.
  *
  * A custom execution context is used here to establish that blocking operations should be
  * executed in a different thread than Play's ExecutionContext, which is used for CPU bound tasks
  * such as rendering.
  */
@Singleton
class PostRepositoryImpl @Inject()()(implicit ec: PostExecutionContext)
    extends PostRepository {

  private val logger = Logger(this.getClass)

  private val postList = List(
    PostData(PostId("1"), "title 1", "blog post 1"),
    PostData(PostId("2"), "title 2", "blog post 2"),
    PostData(PostId("3"), "title 3", "blog post 3"),
    PostData(PostId("4"), "title 4", "blog post 4"),
    PostData(PostId("5"), "title 5", "blog post 5")
  )

  override def list()(
      implicit mc: MarkerContext): Future[Iterable[PostData]] = {
    Future {
      logger.trace(s"list: ")
      postList
    }
  }

  override def get(id: PostId)(
      implicit mc: MarkerContext): Future[Option[PostData]] = {
    Future {
      logger.trace(s"get: id = $id")
      postList.find(post => post.id == id)
    }
  }

  def create(data: PostData)(implicit mc: MarkerContext): Future[PostId] = {
    Future {
      logger.trace(s"create: data = $data")
      data.id
    }
  }

}


text:
package v1.post

import javax.inject.Inject

import play.api.routing.Router.Routes
import play.api.routing.SimpleRouter
import play.api.routing.sird._

/**
  * Routes and URLs to the PostResource controller.
  */
class PostRouter @Inject()(controller: PostController) extends SimpleRouter {
  val prefix = "/v1/posts"

  def link(id: PostId): String = {
    import com.netaporter.uri.dsl._
    val url = prefix / id.toString
    url.toString()
  }

  override def routes: Routes = {
    case GET(p"/") =>
      controller.index

    case POST(p"/") =>
      controller.process

    case GET(p"/$id") =>
      controller.show(id)
  }

}


text:
import org.scalatestplus.play._
import org.scalatestplus.play.guice._
import play.api.libs.json.{ JsResult, Json }
import play.api.mvc.{ RequestHeader, Result }
import play.api.test._
import play.api.test.Helpers._
import play.api.test.CSRFTokenHelper._
import v1.post.PostResource

import scala.concurrent.Future

class PostRouterSpec extends PlaySpec with GuiceOneAppPerTest {

  "PostRouter" should {

    "render the list of posts" in {
      val request = FakeRequest(GET, "/v1/posts").withHeaders(HOST -> "localhost:9000").withCSRFToken
      val home:Future[Result] = route(app, request).get

      val posts: Seq[PostResource] = Json.fromJson[Seq[PostResource]](contentAsJson(home)).get
      posts.filter(_.id == "1").head mustBe (PostResource("1","/v1/posts/1", "title 1", "blog post 1" ))

    }

  }

}


text:
distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME


text:
package simulation

import io.gatling.core.Predef._
import io.gatling.core.structure.{ChainBuilder, ScenarioBuilder}
import io.gatling.http.Predef._
import io.gatling.http.protocol.HttpProtocolBuilder

import scala.concurrent.duration._
import scala.language.postfixOps

// run with "sbt gatling:test" on another machine so you don't have resources contending.
// http://gatling.io/docs/2.2.2/general/simulation_structure.html#simulation-structure
class GatlingSpec extends Simulation {

  // change this to another machine, make sure you have Play running in producion mode
  // i.e. sbt stage / sbt dist and running the script
  val httpConf: HttpProtocolBuilder = http.baseUrl("http://localhost:9000/v1/posts")

  val readClients: ScenarioBuilder = scenario("Clients").exec(Index.refreshManyTimes)

  setUp(
    // For reference, this hits 25% CPU on a 5820K with 32 GB, running both server and load test.
    // In general, you want to ramp up load slowly, and measure with a JVM that has been "warmed up":
    // https://groups.google.com/forum/#!topic/gatling/mD15aj-fyo4
    readClients.inject(rampUsers(10000).during(100.seconds)).protocols(httpConf)
  )
}

object Index {

  def refreshAfterOneSecond: ChainBuilder =
    exec(http("Index").get("/").check(status.is(200))).pause(1)

  val refreshManyTimes: ChainBuilder = repeat(10000) {
    refreshAfterOneSecond
  }
}


text:
<!-- https://www.playframework.com/documentation/latest/SettingsLogger -->
<configuration>

  <conversionRule conversionWord="coloredLevel" converterClass="play.api.libs.logback.ColoredLevel" />

  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>${application.home:-.}/logs/application.log</file>
    <encoder>
      <pattern>%date [%level] from %logger in %thread - %message%n%xException</pattern>
    </encoder>
  </appender>

  <appender name="JSON" class="ch.qos.logback.core.FileAppender">
    <file>${application.home:-.}/logs/application.json</file>
    <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
      <providers>
        <timestamp/>
        <logLevel/>
        <loggerName/>
        <threadName/>
        <message/>
        <logstashMarkers/>
        <arguments/>
        <stackTrace/>
      </providers>
    </encoder>
  </appender>

  <appender name="METRICS" class="ch.qos.logback.core.FileAppender">
    <file>${application.home:-.}/logs/metrics.log</file>
    <encoder>
      <pattern>%date [%level] from %logger in %thread - %message%n%xException</pattern>
    </encoder>
  </appender>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%coloredLevel %logger{15} - %message%n%xException{10}</pattern>
    </encoder>
  </appender>

  <appender name="ASYNCFILE" class="ch.qos.logback.classic.AsyncAppender">
    <appender-ref ref="FILE" />
  </appender>

  <appender name="ASYNCJSON" class="ch.qos.logback.classic.AsyncAppender">
    <appender-ref ref="JSON" />
  </appender>

  <appender name="ASYNCSTDOUT" class="ch.qos.logback.classic.AsyncAppender">
    <appender-ref ref="STDOUT" />
  </appender>

  <logger name="play" level="INFO" />
  <logger name="application" level="DEBUG" />
  <logger name="v1.post" level="TRACE" />

  <root level="WARN">
    <appender-ref ref="ASYNCFILE" />
    <appender-ref ref="ASYNCJSON" />
    <appender-ref ref="ASYNCSTDOUT" />
  </root>

</configuration>


text:
# Set up Play for HTTPS and locked down allowed hosts.
# Nothing in here is required for REST, but it's a good default.
play {
  http {
    cookies.strict = true

    session.secure = true
    session.httpOnly = true

    flash.secure = true
    flash.httpOnly = true

    forwarded.trustedProxies = ["::1", "127.0.0.1"]
  }

  i18n {
    langCookieSecure = true
    langCookieHttpOnly = true
  }

  filters {
    csrf {
      cookie.secure = true
    }

    hosts {
      allowed = ["localhost:9443", "localhost:9000"]
    }

    hsts {
      maxAge = 1 minute # don't interfere with other projects
      secureHost = "localhost"
      securePort = 9443
    }
  }
}


text:
include "secure"

# db connections = ((physical_core_count * 2) + effective_spindle_count)
fixedConnectionPool = 5

repository.dispatcher {
  executor = "thread-pool-executor"
  throughput = 1
  thread-pool-executor {
    fixed-pool-size = ${fixedConnectionPool}
  }
}