[
  {
    "path": "README.md",
    "content": "# blog-related\n"
  },
  {
    "path": "cairosvg-on-alpine/.gitignore",
    "content": ".vscode/"
  },
  {
    "path": "cairosvg-on-alpine/Pipfile",
    "content": "[[source]]\n\nurl = \"https://pypi.python.org/simple\"\nverify_ssl = true\nname = \"pypi\"\n\n[packages]\n\nflask = \"==0.12.2\"\nCairoSVG = \"==2.1.3\"\n\n[dev-packages]\n\n[requires]\n\npython_version = \"3.6\"\n"
  },
  {
    "path": "cairosvg-on-alpine/README.md",
    "content": "# Alpine Image with CairoSVG\n\n```bash\n# build and start the docker container\ndocker-compose up\n# trigger the svg convertion\ncurl http://localhost:5000/image\n```\n\nFor details, check out the `src/Dockerfile`.\n\n# Run the Script on Ubuntu\n\n```bash\n# pip install pipenv\npipenv install\npipenv shell\ncd src\npython svg-converter-service.py\n```\n\nThat should do the trick (tested on Ubuntu 17.04). However, somehow I used to get the error `ModuleNotFoundError: No module named 'PIL'`.\n\n- Fix a) add the dependency `Pillow = \"==5.1.0\"` to the Pipfile\n- or Fix b) \n\n```bash\nsudo apt-get install python3-dev python3-setuptools\nsudo apt-get install python3-dev python3-setuptools libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.5-dev tk8.5-dev\n```\n"
  },
  {
    "path": "cairosvg-on-alpine/docker-compose.yml",
    "content": "version: '3'\nservices:\n  svg-converter-service:\n    build: ./src\n    ports:\n      - \"5000:5000\""
  },
  {
    "path": "cairosvg-on-alpine/src/Dockerfile",
    "content": "FROM python:3.6.4-alpine3.7\n\nRUN apk add --no-cache \\\n    build-base cairo-dev cairo cairo-tools \\\n    # pillow dependencies\n    jpeg-dev zlib-dev freetype-dev lcms2-dev openjpeg-dev tiff-dev tk-dev tcl-dev\n\nRUN pip install \"flask==1.0.1\" \"CairoSVG==2.1.3\"\n\nCOPY circle.svg /\nCOPY svg-converter-service.py /\nCMD python3 /svg-converter-service.py"
  },
  {
    "path": "cairosvg-on-alpine/src/svg-converter-service.py",
    "content": "import cairosvg\nfrom flask import Flask, Response\n\napp = Flask(__name__)\n\n@app.route('/image')\ndef convert_image():\n    png_data = cairosvg.svg2png(url=\"circle.svg\", parent_width=300, parent_height=300)\n    return Response(png_data, mimetype='image/png')\n\nif __name__ == '__main__':\n    app.run(debug=True, port=5000, host='0.0.0.0')"
  },
  {
    "path": "cleaner-code-with-kotlin/.gitignore",
    "content": ".idea/\ntarget/\n*.iml"
  },
  {
    "path": "cleaner-code-with-kotlin/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>de.philipphauer.blog</groupId>\n    <artifactId>cleaner-code-with-kotlin</artifactId>\n    <version>1.0-SNAPSHOT</version>\n\n    <properties>\n        <kotlin.version>1.1.1</kotlin.version>\n        <java.version>1.8</java.version>\n        <okhttp.version>3.6.0</okhttp.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.jetbrains.kotlin</groupId>\n            <artifactId>kotlin-stdlib-jre8</artifactId>\n            <version>${kotlin.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.jetbrains.kotlin</groupId>\n            <artifactId>kotlin-test</artifactId>\n            <version>${kotlin.version}</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.json</groupId>\n            <artifactId>json</artifactId>\n            <version>20160810</version>\n        </dependency>\n        <dependency>\n            <groupId>org.jetbrains.kotlin</groupId>\n            <artifactId>kotlin-stdlib-jre8</artifactId>\n            <version>${kotlin.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>com.squareup.okhttp3</groupId>\n            <artifactId>okhttp</artifactId>\n            <version>${okhttp.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>com.squareup.okhttp3</groupId>\n            <artifactId>logging-interceptor</artifactId>\n            <version>${okhttp.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.assertj</groupId>\n            <artifactId>assertj-core</artifactId>\n            <version>3.6.2</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <version>4.12</version>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>\n        <testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>\n        <plugins>\n            <plugin>\n                <artifactId>kotlin-maven-plugin</artifactId>\n                <groupId>org.jetbrains.kotlin</groupId>\n                <version>${kotlin.version}</version>\n                <executions>\n                    <execution>\n                        <id>compile</id>\n                        <phase>compile</phase>\n                        <goals>\n                            <goal>compile</goal>\n                        </goals>\n                    </execution>\n                    <execution>\n                        <id>test-compile</id>\n                        <phase>test-compile</phase>\n                        <goals>\n                            <goal>test-compile</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <version>3.5.1</version>\n                <configuration>\n                    <source>${java.version}</source>\n                    <target>${java.version}</target>\n                </configuration>\n                <executions>\n                    <!-- Replacing default-compile as it is treated specially by maven -->\n                    <execution>\n                        <id>default-compile</id>\n                        <phase>none</phase>\n                    </execution>\n                    <!-- Replacing default-testCompile as it is treated specially by maven -->\n                    <execution>\n                        <id>default-testCompile</id>\n                        <phase>none</phase>\n                    </execution>\n                    <execution>\n                        <id>java-compile</id>\n                        <phase>compile</phase>\n                        <goals>\n                            <goal>compile</goal>\n                        </goals>\n                    </execution>\n                    <execution>\n                        <id>java-test-compile</id>\n                        <phase>test-compile</phase>\n                        <goals>\n                            <goal>testCompile</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>"
  },
  {
    "path": "cleaner-code-with-kotlin/src/main/kotlin/functions/BeAware.kt",
    "content": "package functions\n\nfun add(value: String?){\n    val map = mutableMapOf<String, String>()\n\n\nvalue?.emptyToNull()?.let { map.put(\"bla\", it) }\n\nif (value?.isNotEmpty() ?: false){\n    map.put(\"key\", value!!)\n}\n\n\n    \n    //KISS\nif (!value.isNullOrEmpty()){\n    map.put(\"key\", value!!)\n}\nif (value != null && value.isNotEmpty()){\n    map.put(\"key\", value)\n}\n// or with smart-cast/without null-assertion\n}\n\nfun  String.emptyToNull() = if (this.isEmpty()) null else this\n    "
  },
  {
    "path": "cleaner-code-with-kotlin/src/main/kotlin/functions/Expressions.kt",
    "content": "package functions\n\nimport org.json.JSONException\nimport org.json.JSONObject\n\nval url = \"http://bla.de?asdf\"\n\nval delimiterIndex = url.indexOf(\"?\")\nval strippedUrl = if (delimiterIndex > 0) {\n    url.substring(0, delimiterIndex)\n} else {\n    url\n}\n\nval json = \"\"\"{\"message\":\"HELLO\"}\"\"\"\nval message = try {\n    JSONObject(json).getString(\"message\")\n} catch (ex: JSONException) {\n    json\n}\n\nfun getMessage(json: String): String {\n    val message: String = try {\n        JSONObject(json).getString(\"message\")\n    } catch (ex: JSONException) {\n        json\n    }\n    return message\n}\nfun getMessage2(json: String) = try {\n    JSONObject(json).getString(\"message\")\n} catch (ex: JSONException) {\n    json\n}\n"
  },
  {
    "path": "cleaner-code-with-kotlin/src/main/kotlin/functions/Immutability.kt",
    "content": "package functions\n\nfun varVal(){\n\n    val id = 1\n    // id = 2\n\n    var id2 = 1\n    id2 = 2\n\n    println(id)\n    println(id2)\n}\n\nfun collections() {\nval list = listOf(1,2,3,4)\n//list.add(1)\nval evenList = list.filter { it % 2 == 0 }\n}\n\n\ndata class DesignMetaData(\n        val id: Int,\n        val fileName: String,\n        val uploaderId: Int,\n        val width: Int = 0,\n        val height: Int = 0\n)\nval design = DesignMetaData(id = 1, fileName = \"cat.jpg\", uploaderId = 2)\nval id = design.id\n// design.id = 2\nval design2 = design.copy(fileName = \"dog.jpg\")\n\nenum class DesignType {PIXEL, VECTOR}"
  },
  {
    "path": "cleaner-code-with-kotlin/src/main/kotlin/functions/Nullability.kt",
    "content": "package functions\n\nval value: String = \"Clean Code\"\n//val value2: String = null\n\nval nullValue: String? = \"Clean Code\"\nval nullValue2: String? = null\n\n//val assign1: String = nullValue\nval assign2: String = if (nullValue == null) \"default\" else nullValue //smart-cast\nval assign3: String = nullValue ?: \"default\"\n\n\n\n\ndata class Order(val customer: Customer?)\ndata class Customer(val address: Address?)\ndata class Address(val city: String)\n\nfun ship(order: Order?){\n    //every time you do if-null-checks, hold on.\nif (order == null || order.customer == null || order.customer.address == null){\n    throw IllegalArgumentException(\"Invalid Order\")\n}\nval city = order.customer.address.city\n}\n\nfun ship2(order: Order?){\n    // Often, you can use null-safe call (?.) or the elvis operator (?:) instead.\n    val city = order?.customer?.address?.city ?: throw IllegalArgumentException(\"Invalid Order\")\n}\n\n\ninterface Service\nclass CustomerService : Service {\n    fun getCustomer() {}\n}\n\nfun getMetrics(service: Service){\n    // also hold on for if-type-checks\n    if (service !is CustomerService) {\n        throw IllegalArgumentException(\"No CustomerService\")\n    }\n    service.getCustomer()\n}\n\nfun getMetrics2(service: Service){\n    //check type, (smart-)cast it and throw exception if the type is not the expected one. all in one expression!\n    service as? CustomerService ?: throw IllegalArgumentException(\"No CustomerService\")\n    service.getCustomer()\n}\n\n\nfun foo(order: Order?){\n    // avoid yelling !! where every possible. search for better solutions by verifying the variable up front and handle nulls. (quote book)\n    order!!.customer!!.address!!.city\n}\n\nfun findOrder(): Order? {\n    return null\n}\nfun dun(customer: Customer?){\n\n}\n\nfun handle(){\n    // Don't\n    val order: Order? = findOrder()\n    if (order != null){\n        dun(order.customer)\n    }\n\n    // with let(), there is no need for an extra variable\n    // can write as one expression\n    findOrder()?.let { dun(it.customer) }\n    findOrder()?.customer?.let(::dun)\n\n}"
  },
  {
    "path": "cleaner-code-with-kotlin/src/main/kotlin/functions/ProductClient.java",
    "content": "package functions;\n\nimport okhttp3.Response;\nimport okhttp3.ResponseBody;\n\npublic class ProductClient {\n\n    public Product parseProductFromHttpBody(Response response){\n        if (response == null){\n            throw new ProductClientException(\"Response is null!\");\n        }\n        int code = response.code();\n        if (code == 200 || code == 201){\n            return mapToDTO(response.body());\n        }\n        if (code >= 400 && code <= 499){\n            throw new ProductClientException(\"Send an invalid request.\");\n        }\n        if (code >= 500 && code <= 599){\n            throw new ProductClientException(\"Server error.\");\n        }\n        throw new ProductClientException(\"Unknown code \" + code);\n    }\n\n    private Product mapToDTO(ResponseBody body) {\n        return null;\n    }\n\n    public static class Product{\n\n    }\n\n    public static class ProductClientException extends RuntimeException{\n        public ProductClientException(String message) {\n            super(message);\n        }\n    }\n}\n"
  },
  {
    "path": "cleaner-code-with-kotlin/src/main/kotlin/functions/ProductClientKotlin.kt",
    "content": "package functions\n\nimport functions.ProductClient.Product\nimport functions.ProductClient.ProductClientException\nimport okhttp3.Response\nimport okhttp3.ResponseBody\n\nclass ProductClientKotlin {\n\n    fun parseProductFromHttpBody(response: Response?) = when (response?.code()){\n        null -> throw ProductClientException(\"Response is null!\")\n        200, 201 -> mapToDTO(response.body())\n        in 400..499 -> throw ProductClientException(\"Send an invalid request.\")\n        in 500..599 -> throw ProductClientException(\"Server error.\")\n        else -> throw ProductClientException(\"Error. Code ${response.code()}\")\n    }\n\n    private fun mapToDTO(body: ResponseBody?): Product {\n        return Product()\n    }\n\n    fun parseProductFromHttpBody2(response: Response?): Product {\n        val product = when (response?.code()) {\n            null -> throw ProductClientException(\"Response is null!\")\n            200, 201 -> mapToDTO(response.body())\n            in 400..499 -> throw ProductClientException(\"Send an invalid request to server.\")\n            in 500..599 -> throw ProductClientException(\"Server error.\")\n            else -> throw ProductClientException(\"Error. Code ${response.code()}\")\n        }\n        return product\n    }\n}\n"
  },
  {
    "path": "compare-payloads/.gitignore",
    "content": "target\n.idea\n*iml\ncompare-scripts/payload-output"
  },
  {
    "path": "compare-payloads/README.md",
    "content": "```\n$ mvn package\n$ java -jar target/compare-payloads_1.jar # TODO\n$ cd compare-scripts\n$ sudo apt install httpie libxml2-utils jq meld\n$ ./compare-json-payload.jh\n$ ./compare-xml-payload.sh\n$ ...\n\n```"
  },
  {
    "path": "compare-payloads/compare-scripts/compare-all-final.sh",
    "content": "#!/usr/bin/env bash\n\nif [ -z \"$2\" ]; then\n    fileName=$(basename \"$0\")\n    printf \"Paths are not provided! Pattern: \\n./$fileName \\\"http://localhost:8080/blogposts\\\" \\\"http://localhost:8080/blogposts2\\\"\\n\"\n    exit\nfi\n\noutputFolder=\"payload-output\"\nif [[ ! -e \"$outputFolder\" ]]; then\n    mkdir \"$outputFolder\"\nfi\n\nresource1=\"$1\"\nresource2=\"$2\"\n\ncompare-payloads(){\n    format=\"$1\"\n    payload1=\"$outputFolder/payload1.$format\"\n    payload2=\"$outputFolder/payload2.$format\"\n\n     if [ \"$format\" = \"json\" ]; then\n        http \"$resource1\" Accept:application/json --pretty=none | jq -S . > \"$payload1\"\n        http \"$resource2\" Accept:application/json --pretty=none | jq -S . > \"$payload2\"\n    else\n        http \"$resource1\" Accept:application/xml --pretty=none | xmllint -c14n - | xmllint --format - > \"$payload1\"\n        http \"$resource2\" Accept:application/xml --pretty=none | xmllint -c14n - | xmllint --format - > \"$payload2\"\n    fi\n    if [ \"$(cat \"$payload1\")\" = \"$(cat \"$payload2\")\" ]; then\n        echo \"$format payloads are equal.\"\n    else\n        echo \"!!!$format payloads are NOT EQUAL!!!\"\n        # if the payloads are not equal, create a diff and show it to the developer. Let him decide.\n        echo \"Showing a diff via meld...\"\n        meld \"$payload1\" \"$payload2\"\n    fi\n}\n#TODO pipe output of http to variable, so this can be outside of the if-body. this also prevents redundant echos for the requests. \"calling ...\".\n#TODO xml nodes are not sorted!\n#TODO compare files... using cat?\n\ncompare-payloads \"json\"\ncompare-payloads \"xml\""
  },
  {
    "path": "compare-payloads/compare-scripts/compare-by-sorting.sh",
    "content": "#!/usr/bin/env bash\n\nif [[ ! -e \"payload-output\" ]]; then\n    mkdir \"payload-output\"\nfi\n\nhttp http://localhost:8080/blogposts --pretty=none > payload-output/version1.json\nhttp http://localhost:8080/blogposts2 --pretty=none > payload-output/version2.json\n# heuristic: sort all characters of the payload alphanumerical. This leads to rubbish.\n# But we can compare the rubbish to determine equality with a high probability.\nversion1Sorted=$(grep -o . \"payload-output/version1.json\" | sort | tr -d \"\\n\")\nversion2Sorted=$(grep -o . \"payload-output/version2.json\" | sort | tr -d \"\\n\")\nif [ \"$version1Sorted\" = \"$version2Sorted\" ]; then\n    echo \"JSON payloads are equal.\"\nelse\n    echo \"!!!JSON payloads are NOT EQUAL!!!\"\n    meld payload-output/version1.json payload-output/version2.json\nfi"
  },
  {
    "path": "compare-payloads/compare-scripts/compare-json-payload.sh",
    "content": "#!/usr/bin/env bash\n\nif [[ ! -e \"payload-output\" ]]; then\n    mkdir \"payload-output\"\nfi\n\nhttp http://localhost:8080/blogposts --pretty=none | jq -S . > payload-output/version1.json\nhttp http://localhost:8080/blogposts2 --pretty=none | jq -S . > payload-output/version2.json\nmeld payload-output/version1.json payload-output/version2.json\n"
  },
  {
    "path": "compare-payloads/compare-scripts/compare-xml-payload.sh",
    "content": "#!/usr/bin/env bash\n\nif [[ ! -e \"payload-output\" ]]; then\n    mkdir \"payload-output\"\nfi\n\nhttp http://localhost:8080/blogposts Accept:application/xml --pretty=none | xmllint -c14n - | xmllint --format - > payload-output/version1.xml\nhttp http://localhost:8080/blogposts2 Accept:application/xml --pretty=none | xmllint -c14n - | xmllint --format - > payload-output/version2.xml\nmeld payload-output/version1.xml payload-output/version2.xml\n#TODO xml nodes are not sorted!\n#TODO difference between canonical format 1.0 and 1.1"
  },
  {
    "path": "compare-payloads/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>de.philipphauer.blog</groupId>\n\t<artifactId>compare-payloads</artifactId>\n\t<version>1</version>\n\t<packaging>jar</packaging>\n\n\t<name>compare-payloads</name>\n\t<description></description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.3.6.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.fasterxml.jackson.dataformat</groupId>\n\t\t\t<artifactId>jackson-dataformat-xml</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.fasterxml.jackson.datatype</groupId>\n\t\t\t<artifactId>jackson-datatype-jsr310</artifactId>\n\t\t\t<version>2.6.1</version>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\n\t</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "compare-payloads/src/main/java/de/philipphauer/blog/BlogPost.java",
    "content": "package de.philipphauer.blog;\n\nimport javax.xml.bind.annotation.XmlRootElement;\nimport java.time.Instant;\nimport java.time.LocalDateTime;\nimport java.time.ZonedDateTime;\n\npublic class BlogPost {\n\n    private String author;\n    private String content;\n    private Instant created;\n\n    public String getAuthor() {\n        return author;\n    }\n\n    public BlogPost setAuthor(String author) {\n        this.author = author;\n        return this;\n    }\n\n    public String getContent() {\n        return content;\n    }\n\n    public BlogPost setContent(String content) {\n        this.content = content;\n        return this;\n    }\n\n    public Instant getCreated() {\n        return created;\n    }\n\n    public BlogPost setCreated(Instant created) {\n        this.created = created;\n        return this;\n    }\n}\n"
  },
  {
    "path": "compare-payloads/src/main/java/de/philipphauer/blog/BlogPost2.java",
    "content": "package de.philipphauer.blog;\n\nimport javax.xml.bind.annotation.XmlRootElement;\n\npublic class BlogPost2 {\n\n    private String authorName;\n    private String created;\n    private String content;\n\n    public String getAuthorName() {\n        return authorName;\n    }\n\n    public BlogPost2 setAuthorName(String authorName) {\n        this.authorName = authorName;\n        return this;\n    }\n\n    public String getContent() {\n        return content;\n    }\n\n    public BlogPost2 setContent(String content) {\n        this.content = content;\n        return this;\n    }\n\n    public String getCreated() {\n        return created;\n    }\n\n    public BlogPost2 setCreated(String created) {\n        this.created = created;\n        return this;\n    }\n}\n"
  },
  {
    "path": "compare-payloads/src/main/java/de/philipphauer/blog/ComparePayloadApplication.java",
    "content": "package de.philipphauer.blog;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.time.Instant;\nimport java.time.ZoneOffset;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.ArrayList;\nimport java.util.List;\n\n@SpringBootApplication\n@RestController\npublic class ComparePayloadApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(ComparePayloadApplication.class, args);\n\t}\n\n    List<BlogPost> blogPosts = new ArrayList<>();\n    List<BlogPost2> blogPosts2 = new ArrayList<>();\n\n    public ComparePayloadApplication(){\n        Instant now = Instant.now();\n        blogPosts.add(new BlogPost().setAuthor(\"Philipp\").setContent(\"Super Post\").setCreated(now));\n        blogPosts.add(new BlogPost().setAuthor(\"Peter\").setContent(\"Nice Post\").setCreated(now.minusSeconds(60)));\n        blogPosts.add(new BlogPost().setAuthor(\"Albert\").setContent(\"Great Post\").setCreated(now.minusSeconds(60*60)));\n        blogPosts2.add(new BlogPost2().setAuthorName(\"Philipp\").setContent(\"Super Post\").setCreated(format(now)));\n        blogPosts2.add(new BlogPost2().setAuthorName(\"Peter\").setContent(\"Nice Post\").setCreated(format(now.minusSeconds(60))));\n        blogPosts2.add(new BlogPost2().setAuthorName(\"Albert\").setContent(\"Great Post\").setCreated(format(now.minusSeconds(60*60))));\n    }\n\n\t@RequestMapping(value = \"/blogposts\", produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})\n\tpublic List<BlogPost> getBlogPosts() {\n        return blogPosts;\n\t}\n\n    @RequestMapping(value = \"/blogposts2\", produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})\n    public List<BlogPost2> getBlogPosts2() {\n        return blogPosts2;\n    }\n\n    public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_OFFSET_DATE_TIME;\n\n    private String format(Instant now) {\n        ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(now, ZoneOffset.UTC);\n        return FORMATTER.format(zonedDateTime);\n    }\n}\n"
  },
  {
    "path": "compare-payloads/src/main/resources/application.properties",
    "content": ""
  },
  {
    "path": "continuation-token/.gitignore",
    "content": ".idea/\n*.iml\n"
  },
  {
    "path": "continuation-token/README.md",
    "content": "# REST API Pagination Example Application\n\n# Getting Started\n\n```bash\ncd continuation-token\n./mvnw install\ncd ../demo-kotlin\n./mvnw package && java -jar target/demo-kotlin*.jar\n```\n\nOpen `http://localhost:8000/designs?pageSize=3` in your browser an click on the URL in the `nextPage` field in the json payload.\n\n# The Application\n\nIt's a lightweight HTTP service written in Kotlin and powered by [HTTP4K](https://www.http4k.org/). It starts within 600 ms. ;-) \n"
  },
  {
    "path": "continuation-token/continuation-token/.gitignore",
    "content": "target/\n!.mvn/wrapper/maven-wrapper.jar\n.idea/\n*.iml"
  },
  {
    "path": "continuation-token/continuation-token/.mvn/wrapper/maven-wrapper.properties",
    "content": "distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.2/apache-maven-3.5.2-bin.zip\n"
  },
  {
    "path": "continuation-token/continuation-token/mvnw",
    "content": "#!/bin/sh\n# ----------------------------------------------------------------------------\n# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\n# ----------------------------------------------------------------------------\n\n# ----------------------------------------------------------------------------\n# Maven2 Start Up Batch script\n#\n# Required ENV vars:\n# ------------------\n#   JAVA_HOME - location of a JDK home dir\n#\n# Optional ENV vars\n# -----------------\n#   M2_HOME - location of maven2's installed home dir\n#   MAVEN_OPTS - parameters passed to the Java VM when running Maven\n#     e.g. to debug Maven itself, use\n#       set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000\n#   MAVEN_SKIP_RC - flag to disable loading of mavenrc files\n# ----------------------------------------------------------------------------\n\nif [ -z \"$MAVEN_SKIP_RC\" ] ; then\n\n  if [ -f /etc/mavenrc ] ; then\n    . /etc/mavenrc\n  fi\n\n  if [ -f \"$HOME/.mavenrc\" ] ; then\n    . \"$HOME/.mavenrc\"\n  fi\n\nfi\n\n# OS specific support.  $var _must_ be set to either true or false.\ncygwin=false;\ndarwin=false;\nmingw=false\ncase \"`uname`\" in\n  CYGWIN*) cygwin=true ;;\n  MINGW*) mingw=true;;\n  Darwin*) darwin=true\n    # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home\n    # See https://developer.apple.com/library/mac/qa/qa1170/_index.html\n    if [ -z \"$JAVA_HOME\" ]; then\n      if [ -x \"/usr/libexec/java_home\" ]; then\n        export JAVA_HOME=\"`/usr/libexec/java_home`\"\n      else\n        export JAVA_HOME=\"/Library/Java/Home\"\n      fi\n    fi\n    ;;\nesac\n\nif [ -z \"$JAVA_HOME\" ] ; then\n  if [ -r /etc/gentoo-release ] ; then\n    JAVA_HOME=`java-config --jre-home`\n  fi\nfi\n\nif [ -z \"$M2_HOME\" ] ; then\n  ## resolve links - $0 may be a link to maven's home\n  PRG=\"$0\"\n\n  # need this for relative symlinks\n  while [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n      PRG=\"$link\"\n    else\n      PRG=\"`dirname \"$PRG\"`/$link\"\n    fi\n  done\n\n  saveddir=`pwd`\n\n  M2_HOME=`dirname \"$PRG\"`/..\n\n  # make it fully qualified\n  M2_HOME=`cd \"$M2_HOME\" && pwd`\n\n  cd \"$saveddir\"\n  # echo Using m2 at $M2_HOME\nfi\n\n# For Cygwin, ensure paths are in UNIX format before anything is touched\nif $cygwin ; then\n  [ -n \"$M2_HOME\" ] &&\n    M2_HOME=`cygpath --unix \"$M2_HOME\"`\n  [ -n \"$JAVA_HOME\" ] &&\n    JAVA_HOME=`cygpath --unix \"$JAVA_HOME\"`\n  [ -n \"$CLASSPATH\" ] &&\n    CLASSPATH=`cygpath --path --unix \"$CLASSPATH\"`\nfi\n\n# For Migwn, ensure paths are in UNIX format before anything is touched\nif $mingw ; then\n  [ -n \"$M2_HOME\" ] &&\n    M2_HOME=\"`(cd \"$M2_HOME\"; pwd)`\"\n  [ -n \"$JAVA_HOME\" ] &&\n    JAVA_HOME=\"`(cd \"$JAVA_HOME\"; pwd)`\"\n  # TODO classpath?\nfi\n\nif [ -z \"$JAVA_HOME\" ]; then\n  javaExecutable=\"`which javac`\"\n  if [ -n \"$javaExecutable\" ] && ! [ \"`expr \\\"$javaExecutable\\\" : '\\([^ ]*\\)'`\" = \"no\" ]; then\n    # readlink(1) is not available as standard on Solaris 10.\n    readLink=`which readlink`\n    if [ ! `expr \"$readLink\" : '\\([^ ]*\\)'` = \"no\" ]; then\n      if $darwin ; then\n        javaHome=\"`dirname \\\"$javaExecutable\\\"`\"\n        javaExecutable=\"`cd \\\"$javaHome\\\" && pwd -P`/javac\"\n      else\n        javaExecutable=\"`readlink -f \\\"$javaExecutable\\\"`\"\n      fi\n      javaHome=\"`dirname \\\"$javaExecutable\\\"`\"\n      javaHome=`expr \"$javaHome\" : '\\(.*\\)/bin'`\n      JAVA_HOME=\"$javaHome\"\n      export JAVA_HOME\n    fi\n  fi\nfi\n\nif [ -z \"$JAVACMD\" ] ; then\n  if [ -n \"$JAVA_HOME\"  ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n      # IBM's JDK on AIX uses strange locations for the executables\n      JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n      JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n  else\n    JAVACMD=\"`which java`\"\n  fi\nfi\n\nif [ ! -x \"$JAVACMD\" ] ; then\n  echo \"Error: JAVA_HOME is not defined correctly.\" >&2\n  echo \"  We cannot execute $JAVACMD\" >&2\n  exit 1\nfi\n\nif [ -z \"$JAVA_HOME\" ] ; then\n  echo \"Warning: JAVA_HOME environment variable is not set.\"\nfi\n\nCLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher\n\n# traverses directory structure from process work directory to filesystem root\n# first directory with .mvn subdirectory is considered project base directory\nfind_maven_basedir() {\n\n  if [ -z \"$1\" ]\n  then\n    echo \"Path not specified to find_maven_basedir\"\n    return 1\n  fi\n\n  basedir=\"$1\"\n  wdir=\"$1\"\n  while [ \"$wdir\" != '/' ] ; do\n    if [ -d \"$wdir\"/.mvn ] ; then\n      basedir=$wdir\n      break\n    fi\n    # workaround for JBEAP-8937 (on Solaris 10/Sparc)\n    if [ -d \"${wdir}\" ]; then\n      wdir=`cd \"$wdir/..\"; pwd`\n    fi\n    # end of workaround\n  done\n  echo \"${basedir}\"\n}\n\n# concatenates all lines of a file\nconcat_lines() {\n  if [ -f \"$1\" ]; then\n    echo \"$(tr -s '\\n' ' ' < \"$1\")\"\n  fi\n}\n\nBASE_DIR=`find_maven_basedir \"$(pwd)\"`\nif [ -z \"$BASE_DIR\" ]; then\n  exit 1;\nfi\n\nexport MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-\"$BASE_DIR\"}\necho $MAVEN_PROJECTBASEDIR\nMAVEN_OPTS=\"$(concat_lines \"$MAVEN_PROJECTBASEDIR/.mvn/jvm.config\") $MAVEN_OPTS\"\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin; then\n  [ -n \"$M2_HOME\" ] &&\n    M2_HOME=`cygpath --path --windows \"$M2_HOME\"`\n  [ -n \"$JAVA_HOME\" ] &&\n    JAVA_HOME=`cygpath --path --windows \"$JAVA_HOME\"`\n  [ -n \"$CLASSPATH\" ] &&\n    CLASSPATH=`cygpath --path --windows \"$CLASSPATH\"`\n  [ -n \"$MAVEN_PROJECTBASEDIR\" ] &&\n    MAVEN_PROJECTBASEDIR=`cygpath --path --windows \"$MAVEN_PROJECTBASEDIR\"`\nfi\n\nWRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain\n\nexec \"$JAVACMD\" \\\n  $MAVEN_OPTS \\\n  -classpath \"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar\" \\\n  \"-Dmaven.home=${M2_HOME}\" \"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}\" \\\n  ${WRAPPER_LAUNCHER} $MAVEN_CONFIG \"$@\"\n"
  },
  {
    "path": "continuation-token/continuation-token/mvnw.cmd",
    "content": "@REM ----------------------------------------------------------------------------\n@REM Licensed to the Apache Software Foundation (ASF) under one\n@REM or more contributor license agreements.  See the NOTICE file\n@REM distributed with this work for additional information\n@REM regarding copyright ownership.  The ASF licenses this file\n@REM to you under the Apache License, Version 2.0 (the\n@REM \"License\"); you may not use this file except in compliance\n@REM with the License.  You may obtain a copy of the License at\n@REM\n@REM    http://www.apache.org/licenses/LICENSE-2.0\n@REM\n@REM Unless required by applicable law or agreed to in writing,\n@REM software distributed under the License is distributed on an\n@REM \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n@REM KIND, either express or implied.  See the License for the\n@REM specific language governing permissions and limitations\n@REM under the License.\n@REM ----------------------------------------------------------------------------\n\n@REM ----------------------------------------------------------------------------\n@REM Maven2 Start Up Batch script\n@REM\n@REM Required ENV vars:\n@REM JAVA_HOME - location of a JDK home dir\n@REM\n@REM Optional ENV vars\n@REM M2_HOME - location of maven2's installed home dir\n@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands\n@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending\n@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven\n@REM     e.g. to debug Maven itself, use\n@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000\n@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files\n@REM ----------------------------------------------------------------------------\n\n@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'\n@echo off\n@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'\n@if \"%MAVEN_BATCH_ECHO%\" == \"on\"  echo %MAVEN_BATCH_ECHO%\n\n@REM set %HOME% to equivalent of $HOME\nif \"%HOME%\" == \"\" (set \"HOME=%HOMEDRIVE%%HOMEPATH%\")\n\n@REM Execute a user defined script before this one\nif not \"%MAVEN_SKIP_RC%\" == \"\" goto skipRcPre\n@REM check for pre script, once with legacy .bat ending and once with .cmd ending\nif exist \"%HOME%\\mavenrc_pre.bat\" call \"%HOME%\\mavenrc_pre.bat\"\nif exist \"%HOME%\\mavenrc_pre.cmd\" call \"%HOME%\\mavenrc_pre.cmd\"\n:skipRcPre\n\n@setlocal\n\nset ERROR_CODE=0\n\n@REM To isolate internal variables from possible post scripts, we use another setlocal\n@setlocal\n\n@REM ==== START VALIDATION ====\nif not \"%JAVA_HOME%\" == \"\" goto OkJHome\n\necho.\necho Error: JAVA_HOME not found in your environment. >&2\necho Please set the JAVA_HOME variable in your environment to match the >&2\necho location of your Java installation. >&2\necho.\ngoto error\n\n:OkJHome\nif exist \"%JAVA_HOME%\\bin\\java.exe\" goto init\n\necho.\necho Error: JAVA_HOME is set to an invalid directory. >&2\necho JAVA_HOME = \"%JAVA_HOME%\" >&2\necho Please set the JAVA_HOME variable in your environment to match the >&2\necho location of your Java installation. >&2\necho.\ngoto error\n\n@REM ==== END VALIDATION ====\n\n:init\n\n@REM Find the project base dir, i.e. the directory that contains the folder \".mvn\".\n@REM Fallback to current working directory if not found.\n\nset MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%\nIF NOT \"%MAVEN_PROJECTBASEDIR%\"==\"\" goto endDetectBaseDir\n\nset EXEC_DIR=%CD%\nset WDIR=%EXEC_DIR%\n:findBaseDir\nIF EXIST \"%WDIR%\"\\.mvn goto baseDirFound\ncd ..\nIF \"%WDIR%\"==\"%CD%\" goto baseDirNotFound\nset WDIR=%CD%\ngoto findBaseDir\n\n:baseDirFound\nset MAVEN_PROJECTBASEDIR=%WDIR%\ncd \"%EXEC_DIR%\"\ngoto endDetectBaseDir\n\n:baseDirNotFound\nset MAVEN_PROJECTBASEDIR=%EXEC_DIR%\ncd \"%EXEC_DIR%\"\n\n:endDetectBaseDir\n\nIF NOT EXIST \"%MAVEN_PROJECTBASEDIR%\\.mvn\\jvm.config\" goto endReadAdditionalConfig\n\n@setlocal EnableExtensions EnableDelayedExpansion\nfor /F \"usebackq delims=\" %%a in (\"%MAVEN_PROJECTBASEDIR%\\.mvn\\jvm.config\") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a\n@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%\n\n:endReadAdditionalConfig\n\nSET MAVEN_JAVA_EXE=\"%JAVA_HOME%\\bin\\java.exe\"\n\nset WRAPPER_JAR=\"%MAVEN_PROJECTBASEDIR%\\.mvn\\wrapper\\maven-wrapper.jar\"\nset WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain\n\n%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% \"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%\" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*\nif ERRORLEVEL 1 goto error\ngoto end\n\n:error\nset ERROR_CODE=1\n\n:end\n@endlocal & set ERROR_CODE=%ERROR_CODE%\n\nif not \"%MAVEN_SKIP_RC%\" == \"\" goto skipRcPost\n@REM check for post script, once with legacy .bat ending and once with .cmd ending\nif exist \"%HOME%\\mavenrc_post.bat\" call \"%HOME%\\mavenrc_post.bat\"\nif exist \"%HOME%\\mavenrc_post.cmd\" call \"%HOME%\\mavenrc_post.cmd\"\n:skipRcPost\n\n@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'\nif \"%MAVEN_BATCH_PAUSE%\" == \"on\" pause\n\nif \"%MAVEN_TERMINATE_CMD%\" == \"on\" exit %ERROR_CODE%\n\nexit /B %ERROR_CODE%\n"
  },
  {
    "path": "continuation-token/continuation-token/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n\n    <modelVersion>4.0.0</modelVersion>\n\n    <!--TODO groupId, url, developers, github url (see http://central.sonatype.org/pages/requirements.html) -->\n\n    <groupId>de.philipphauer.blog</groupId>\n    <artifactId>continuation-token</artifactId>\n    <version>1.0.0-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Continuation-Token</name>\n    <description>Handy library to implement fast and reliable API pagination</description>\n\n    <licenses>\n        <license>\n            <name>MIT License</name>\n            <url>http://www.opensource.org/licenses/mit-license.php</url>\n        </license>\n    </licenses>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <kotlin.version>1.1.60</kotlin.version>\n        <junit5.version>5.0.2</junit5.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.jetbrains.kotlin</groupId>\n            <artifactId>kotlin-stdlib</artifactId>\n            <version>${kotlin.version}</version>\n        </dependency>\n        <!-- test -->\n        <dependency>\n            <groupId>org.junit.jupiter</groupId>\n            <artifactId>junit-jupiter-api</artifactId>\n            <version>${junit5.version}</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.junit.jupiter</groupId>\n            <artifactId>junit-jupiter-params</artifactId>\n            <version>${junit5.version}</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.assertj</groupId>\n            <artifactId>assertj-core</artifactId>\n            <version>3.8.0</version>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <sourceDirectory>src/main/kotlin</sourceDirectory>\n        <testSourceDirectory>src/test/kotlin</testSourceDirectory>\n\n        <plugins>\n            <plugin>\n                <groupId>org.jetbrains.kotlin</groupId>\n                <artifactId>kotlin-maven-plugin</artifactId>\n                <version>${kotlin.version}</version>\n                <executions>\n                    <execution>\n                        <id>compile</id>\n                        <phase>compile</phase>\n                        <goals>\n                            <goal>compile</goal>\n                        </goals>\n                    </execution>\n                    <execution>\n                        <id>test-compile</id>\n                        <phase>test-compile</phase>\n                        <goals>\n                            <goal>test-compile</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <artifactId>maven-surefire-plugin</artifactId>\n                <version>2.19</version>\n                <dependencies>\n                    <dependency>\n                        <groupId>org.junit.platform</groupId>\n                        <artifactId>junit-platform-surefire-provider</artifactId>\n                        <version>1.0.2</version>\n                    </dependency>\n                    <dependency>\n                        <groupId>org.junit.jupiter</groupId>\n                        <artifactId>junit-jupiter-engine</artifactId>\n                        <version>${junit5.version}</version>\n                    </dependency>\n                </dependencies>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-source-plugin</artifactId>\n                <version>3.0.1</version>\n                <executions>\n                    <execution>\n                        <id>attach-sources</id>\n                        <goals>\n                            <goal>jar</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <groupId>org.jetbrains.dokka</groupId>\n                <artifactId>dokka-maven-plugin</artifactId>\n                <version>0.9.15</version>\n                <executions>\n                    <execution>\n                        <phase>pre-site</phase>\n                        <goals>\n                            <goal>javadocJar</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n    <pluginRepositories>\n        <pluginRepository>\n            <id>jcenter</id>\n            <name>JCenter</name>\n            <url>https://jcenter.bintray.com/</url>\n        </pluginRepository>\n    </pluginRepositories>\n</project>\n"
  },
  {
    "path": "continuation-token/continuation-token/src/main/kotlin/de/philipphauer/blog/pagination/ContinuationTokenParser.kt",
    "content": "package de.philipphauer.blog.pagination\n\nobject ContinuationTokenParser {\n    val DELIMITER = \":\"\n\n    fun parse(tokenString: String): ContinuationToken{\n        val parts = tokenString.split(DELIMITER)\n        try {\n            return ContinuationToken(\n                    timestamp = parts[0].toLong(),\n                    offset = parts[1].toInt(),\n                    checksum = parts[2].toLong()\n            )\n        } catch (ex: Exception){\n            throw ContinuationTokenParseException(\"Invalid token '$tokenString'.\", ex)\n        }\n    }\n}\n\nclass ContinuationTokenParseException(message: String, cause: Exception) : RuntimeException(message, cause)"
  },
  {
    "path": "continuation-token/continuation-token/src/main/kotlin/de/philipphauer/blog/pagination/Model.kt",
    "content": "package de.philipphauer.blog.pagination\n\nimport de.philipphauer.blog.pagination.ContinuationTokenParser.DELIMITER\n\n/** a token points to the last element of the current page. \"last\" usually means \"highest timestamp\". **/\ndata class ContinuationToken(\n        /** timestamp of the highest entity in the last page. */\n        val timestamp: Long,\n        /** offset = amount of entities with the highest timestamp in the last page, that have the same timestamp */\n        val offset: Int,\n        /** used to detect modifications during pagination */\n        val checksum: Long\n) {\n    override fun toString() = \"$timestamp$DELIMITER$offset$DELIMITER$checksum\"\n}\n\ndata class QueryAdvice(\n        /** use this with >= in the WHERE clause (the equals is important!) */\n        val timestamp: Long,\n        val limit: Int\n)\n\ndata class Page(\n        val entities: List<Pageable>,\n        val currentToken: ContinuationToken?\n)\n\ninterface Pageable {\n    fun getID(): String\n    fun getTimestamp(): Long\n}"
  },
  {
    "path": "continuation-token/continuation-token/src/main/kotlin/de/philipphauer/blog/pagination/Pagination.kt",
    "content": "package de.philipphauer.blog.pagination\n\nimport java.util.zip.CRC32\n\nobject Pagination{\n\n    //TODO implement checksum fallback\n\n    fun createPage(entitiesSinceIncludingTs: List<Pageable>, oldToken: ContinuationToken?, requiredPageSize: Int): Page {\n        if (entitiesSinceIncludingTs.isEmpty()){\n            return Page(entities = listOf(), currentToken = null)\n        }\n        if (oldToken == null || currentPageStartsWithADifferentTimestampThanInToken(entitiesSinceIncludingTs, oldToken)){\n            //don't skip\n            val token = createTokenForPage(entitiesSinceIncludingTs, entitiesSinceIncludingTs, requiredPageSize)\n            return Page(entities = entitiesSinceIncludingTs, currentToken = token)\n        } else {\n            val entitiesForNextPage = skipOffset(entitiesSinceIncludingTs, oldToken)\n            val token = createTokenForPage(entitiesSinceIncludingTs, entitiesForNextPage, requiredPageSize)\n            return Page(entities = entitiesForNextPage, currentToken = token)\n        }\n    }\n\n    private fun fillUpWholePage(entities: List<Pageable>, requiredPageSize: Int): Boolean =\n            entities.size >= requiredPageSize\n\n    private fun currentPageStartsWithADifferentTimestampThanInToken(allEntitiesSinceIncludingTs: List<Pageable>, oldToken: ContinuationToken): Boolean {\n        val timestampOfFirstElement = allEntitiesSinceIncludingTs.first().getTimestamp()\n        return timestampOfFirstElement != oldToken.timestamp\n    }\n\n    fun calculateQueryAdvice(token: ContinuationToken?, pageSize: Int): QueryAdvice {\n        if (token == null){\n            return QueryAdvice(limit = pageSize, timestamp = 0)\n        }\n        return QueryAdvice(limit = token.offset + pageSize, timestamp = token.timestamp)\n    }\n\n    private fun skipOffset(entitiesSinceIncludingTs: List<Pageable>, currentToken: ContinuationToken) =\n            entitiesSinceIncludingTs.subList(currentToken.offset, entitiesSinceIncludingTs.size)\n\n    /**\n     * @param entitiesForNextPage includes skip/offset\n     */\n    internal fun createTokenForPage(allEntitiesSinceIncludingTs: List<Pageable>,\n                                    entitiesForNextPage: List<Pageable>,\n                                    requiredPageSize: Int): ContinuationToken? {\n        if (allEntitiesSinceIncludingTs.isEmpty()){\n            return null\n        }\n        if (!fillUpWholePage(entitiesForNextPage, requiredPageSize)){\n            return null // no next token required\n        }\n        val highestEntities = getEntitiesWithHighestTimestamp(allEntitiesSinceIncludingTs)\n        val highestTimestamp = highestEntities.last().getTimestamp()\n        val ids = highestEntities.map(Pageable::getID)\n        val checksum = createCRC32Checksum(ids)\n        return ContinuationToken(\n                timestamp = highestTimestamp,\n                offset = highestEntities.size,\n                checksum = checksum\n        )\n    }\n\n    private fun createCRC32Checksum(ids: List<String>): Long {\n        val hash = CRC32()\n        hash.update(ids.joinToString(\"_\").toByteArray())\n        return hash.value\n    }\n\n    internal fun getEntitiesWithHighestTimestamp(entities: List<Pageable>): List<Pageable> {\n        if (entities.isEmpty()){\n            return listOf()\n        }\n        val highestTimestamp = entities.last().getTimestamp()\n        val entitiesWithHighestTimestamp = mutableListOf<Pageable>()\n\n        val lastIndex = entities.size - 1\n        var i = lastIndex\n        while (i >= 0 && highestTimestamp == entities[i].getTimestamp()) {\n            entitiesWithHighestTimestamp.add(entities[i])\n            i--\n        }\n        return entitiesWithHighestTimestamp.reversed()\n    }\n\n}"
  },
  {
    "path": "continuation-token/continuation-token/src/test/kotlin/de/philipphauer/blog/pagination/ContinuationTokenParserTest.kt",
    "content": "package de.philipphauer.blog.pagination\n\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Assertions.assertThrows\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.TestInstance\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.MethodSource\nimport java.util.stream.Stream\n\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\ninternal class ContinuationTokenParserTest {\n\n    private val parser = ContinuationTokenParser\n\n    @Test\n    fun valid() {\n        assertThat(parser.parse(\"1511443755:2:1842515611\"))\n                .isEqualTo(ContinuationToken(timestamp = 1511443755, offset = 2, checksum = 1842515611))\n        assertThat(parser.parse(\"1511443755:1:1842521611\"))\n                .isEqualTo(ContinuationToken(timestamp = 1511443755, offset = 1, checksum = 1842521611))\n\n        //also support timestamps with millisecond precision\n        assertThat(parser.parse(\"1511443755999:1:1842521611\"))\n                .isEqualTo(ContinuationToken(timestamp = 1511443755999, offset = 1, checksum = 1842521611))\n    }\n\n    @ParameterizedTest\n    @MethodSource(\"invalidTokenProvider\")\n    fun invalid(invalidToken: String) {\n        val exception = assertThrows(ContinuationTokenParseException::class.java){\n            parser.parse(invalidToken)\n        }\n        assertThat(exception.message).isEqualTo(\"Invalid token '$invalidToken'.\")\n    }\n\n    private fun invalidTokenProvider(): Stream<String> = Stream.of(\n            \"asdf:1:1842521611\"\n            , \"1511443755:sadfasd:1842521611\"\n            , \"1511443755:1:sadfasd\"\n            , \"1511443755:1\"\n            , \"1511443755:1:\"\n            , \"\"\n            , \"::\"\n            , \"12::\"\n            , \"12::213\"\n            , \":1231:213\"\n    )\n}\n"
  },
  {
    "path": "continuation-token/continuation-token/src/test/kotlin/de/philipphauer/blog/pagination/PaginationTest.kt",
    "content": "package de.philipphauer.blog.pagination\n\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Nested\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.TestInstance\nimport java.util.zip.CRC32\n\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\ninternal class PaginationTest{\n\n    @Nested\n    inner class `createPage` {\n        @Test\n        fun `|1,2,3|4,5,6| different keys`() {\n            val allEntries = listOf(\n                    TestPageable(1),\n                    TestPageable(2),\n                    TestPageable(3),\n                    TestPageable(4),\n                    TestPageable(5),\n                    TestPageable(6)\n            )\n            val firstPage = allEntries.getEntriesSinceIncluding(timestamp = 0, limit = 3)\n\n            val page = Pagination.createPage(firstPage, null, 3)\n            assertThat(page).isEqualTo(Page(\n                    entities = listOf(\n                            TestPageable(1),\n                            TestPageable(2),\n                            TestPageable(3)\n                    ),\n                    currentToken = ContinuationToken(timestamp = 3, offset = 1, checksum = checksum(\"3\"))\n            ))\n\n            val entriesSinceKey = allEntries.getEntriesSinceIncluding(timestamp = 3, limit = 4)\n            val page2 = Pagination.createPage(entriesSinceKey, page.currentToken, 3)\n            assertThat(page2).isEqualTo(Page(\n                    entities = listOf(\n                            TestPageable(4),\n                            TestPageable(5),\n                            TestPageable(6)\n                    ),\n                    currentToken = ContinuationToken(timestamp = 6, offset = 1, checksum = checksum(\"6\"))\n            ))\n        }\n\n        @Test\n        fun `|1,2,3|3,5,6| key 3 overlaps two pages`() {\n            val allEntries = listOf(\n                    TestPageable(\"1\", 1),\n                    TestPageable(\"2\", 2),\n                    TestPageable(\"3\", 3),\n                    TestPageable(\"4\", 3),\n                    TestPageable(\"5\", 5),\n                    TestPageable(\"6\", 6)\n            )\n            val firstPage = allEntries.getEntriesSinceIncluding(timestamp = 0, limit = 3)\n\n            val page = Pagination.createPage(firstPage, null, 3)\n            assertThat(page).isEqualTo(Page(\n                    entities = listOf(\n                            TestPageable(\"1\", 1),\n                            TestPageable(\"2\", 2),\n                            TestPageable(\"3\", 3)\n                    ),\n                    currentToken = ContinuationToken(timestamp = 3, offset = 1, checksum = checksum(\"3\"))\n            ))\n\n            val entriesSinceKey = allEntries.getEntriesSinceIncluding(timestamp = 3, limit = 4)\n            val page2 = Pagination.createPage(entriesSinceKey, page.currentToken, 3)\n            assertThat(page2).isEqualTo(Page(\n                    entities = listOf(\n                            TestPageable(\"4\", 3),\n                            TestPageable(\"5\", 5),\n                            TestPageable(\"6\", 6)\n                    ),\n                    currentToken = ContinuationToken(timestamp = 6, offset = 1, checksum = checksum(\"6\"))\n            ))\n        }\n\n        @Test\n        fun `|1,1,1|1,1,1| all have same key`() {\n            val allEntries = listOf(\n                    TestPageable(\"1\", 1),\n                    TestPageable(\"2\", 1),\n                    TestPageable(\"3\", 1),\n                    TestPageable(\"4\", 1),\n                    TestPageable(\"5\", 1),\n                    TestPageable(\"6\", 1)\n            )\n            val firstPage = allEntries.getEntriesSinceIncluding(timestamp = 0, limit = 3)\n            val page = Pagination.createPage(firstPage, null, 3)\n            assertThat(page).isEqualTo(Page(\n                    entities = listOf(\n                            TestPageable(\"1\", 1),\n                            TestPageable(\"2\", 1),\n                            TestPageable(\"3\", 1)\n                    ),\n                    currentToken = ContinuationToken(timestamp = 1, offset = 3, checksum = checksum(\"1\", \"2\", \"3\"))\n            ))\n\n            val entriesSinceKey = allEntries.getEntriesSinceIncluding(timestamp = 1, limit = 6)\n            val page2 = Pagination.createPage(entriesSinceKey, page.currentToken, 3)\n            assertThat(page2).isEqualTo(Page(\n                    entities = listOf(\n                            TestPageable(\"4\", 1),\n                            TestPageable(\"5\", 1),\n                            TestPageable(\"6\", 1)\n                    ),\n                    currentToken = ContinuationToken(timestamp = 1, offset = 6, checksum = checksum(\"1\", \"2\", \"3\", \"4\", \"5\", \"6\"))\n            ))\n        }\n\n        @Test\n        fun `|1,2,3|| although it's the last page it fits right into the page size so we can't tell if this is the last page and have to pass a next token`() {\n            val allEntries = listOf(\n                    TestPageable(1),\n                    TestPageable(2),\n                    TestPageable(3)\n            )\n            val firstPage = allEntries.getEntriesSinceIncluding(timestamp = 0, limit = 3)\n\n            val page = Pagination.createPage(firstPage, null, 3)\n            assertThat(page).isEqualTo(Page(\n                    entities = listOf(\n                            TestPageable(1),\n                            TestPageable(2),\n                            TestPageable(3)\n                    ),\n                    currentToken = ContinuationToken(timestamp = 3, offset = 1, checksum = checksum(\"3\"))\n            ))\n\n            val entriesSinceKey = allEntries.getEntriesSinceIncluding(timestamp = 3, limit = 3)\n            val page2 = Pagination.createPage(entriesSinceKey, page.currentToken, 3)\n            assertThat(page2).isEqualTo(Page(\n                    entities = listOf(),\n                    currentToken = null\n            ))\n        }\n\n        @Test\n        fun `|1,2| no next token if there are less elements than page size`() {\n            val allEntries = listOf(\n                    TestPageable(1),\n                    TestPageable(2)\n            )\n            val firstPage = allEntries.getEntriesSinceIncluding(timestamp = 0, limit = 3)\n\n            val page = Pagination.createPage(firstPage, null, 3)\n            assertThat(page).isEqualTo(Page(\n                    entities = listOf(\n                            TestPageable(1),\n                            TestPageable(2)\n                    ),\n                    currentToken = null\n            ))\n        }\n\n        @Test\n        fun `|1,2,3|4| still skip correctly even if there are less elements than page size`() {\n            val allEntries = listOf(\n                    TestPageable(1),\n                    TestPageable(2),\n                    TestPageable(3),\n                    TestPageable(4)\n            )\n            val firstPage = allEntries.getEntriesSinceIncluding(timestamp = 0, limit = 3)\n\n            val page = Pagination.createPage(firstPage, null, 3)\n            assertThat(page).isEqualTo(Page(\n                    entities = listOf(\n                            TestPageable(1),\n                            TestPageable(2),\n                            TestPageable(3)\n                    ),\n                    currentToken = ContinuationToken(timestamp = 3, offset = 1, checksum = checksum(\"3\"))\n            ))\n\n            val entriesSinceKey = allEntries.getEntriesSinceIncluding(timestamp = 3, limit = 3)\n            val page2 = Pagination.createPage(entriesSinceKey, page.currentToken, 3)\n            assertThat(page2).isEqualTo(Page(\n                    entities = listOf(\n                            TestPageable(4)\n                    ),\n                    currentToken = null\n            ))\n        }\n\n        @Test\n        fun `|1,2,3|4,5| second page is not full so no next token`() {\n            val allEntries = listOf(\n                    TestPageable(1),\n                    TestPageable(2),\n                    TestPageable(3),\n                    TestPageable(4),\n                    TestPageable(5)\n            )\n            val firstPage = allEntries.getEntriesSinceIncluding(timestamp = 0, limit = 3)\n\n            val page = Pagination.createPage(firstPage, null, 3)\n            assertThat(page).isEqualTo(Page(\n                    entities = listOf(\n                            TestPageable(1),\n                            TestPageable(2),\n                            TestPageable(3)\n                    ),\n                    currentToken = ContinuationToken(timestamp = 3, offset = 1, checksum = checksum(\"3\"))\n            ))\n\n            val entriesSinceKey = allEntries.getEntriesSinceIncluding(timestamp = 3, limit = 3)\n            val page2 = Pagination.createPage(entriesSinceKey, page.currentToken, 3)\n            assertThat(page2).isEqualTo(Page(\n                    entities = listOf(\n                            TestPageable(4),\n                            TestPageable(5)\n                    ),\n                    currentToken = null\n            ))\n        }\n\n        @Test\n        fun `|| empty page`() {\n            val page = Pagination.createPage(listOf(), null, 3)\n            assertThat(page).isEqualTo(Page(\n                    entities = listOf(),\n                    currentToken = null\n            ))\n        }\n\n        @Test\n        fun `|1,3,3|4,5,6|`() {\n            val allEntries = listOf(\n                    TestPageable(\"1\", 1),\n                    TestPageable(\"2\", 3),\n                    TestPageable(\"3\", 3),\n                    TestPageable(\"4\", 4),\n                    TestPageable(\"5\", 5),\n                    TestPageable(\"6\", 6)\n            )\n            val firstPage = allEntries.getEntriesSinceIncluding(timestamp = 0, limit = 3)\n\n            val page = Pagination.createPage(firstPage, null, 3)\n            assertThat(page).isEqualTo(Page(\n                    entities = listOf(\n                            TestPageable(\"1\", 1),\n                            TestPageable(\"2\", 3),\n                            TestPageable(\"3\", 3)\n                    ),\n                    currentToken = ContinuationToken(timestamp = 3, offset = 2, checksum = checksum(\"2\", \"3\"))\n            ))\n\n            val entriesSinceKey = allEntries.getEntriesSinceIncluding(timestamp = 3, limit = 5)\n            val page2 = Pagination.createPage(entriesSinceKey, page.currentToken, 3)\n            assertThat(page2).isEqualTo(Page(\n                    entities = listOf(\n                            TestPageable(\"4\", 4),\n                            TestPageable(\"5\", 5),\n                            TestPageable(\"6\", 6)\n                    ),\n                    currentToken = ContinuationToken(timestamp = 6, offset = 1, checksum = checksum(\"6\"))\n            ))\n        }\n\n        @Test\n        fun `|1,3,3|3,5,6|`() {\n            val allEntries = listOf(\n                    TestPageable(\"1\", 1),\n                    TestPageable(\"2\", 3),\n                    TestPageable(\"3\", 3),\n                    TestPageable(\"4\", 3),\n                    TestPageable(\"5\", 5),\n                    TestPageable(\"6\", 6)\n            )\n            val firstPage = allEntries.getEntriesSinceIncluding(timestamp = 0, limit = 3)\n\n            val page = Pagination.createPage(firstPage, null, 3)\n            assertThat(page).isEqualTo(Page(\n                    entities = listOf(\n                            TestPageable(\"1\", 1),\n                            TestPageable(\"2\", 3),\n                            TestPageable(\"3\", 3)\n                    ),\n                    currentToken = ContinuationToken(timestamp = 3, offset = 2, checksum = checksum(\"2\", \"3\"))\n            ))\n\n            val entriesSinceKey = allEntries.getEntriesSinceIncluding(timestamp = 3, limit = 5)\n            val page2 = Pagination.createPage(entriesSinceKey, page.currentToken, 3)\n            assertThat(page2).isEqualTo(Page(\n                    entities = listOf(\n                            TestPageable(\"4\", 3),\n                            TestPageable(\"5\", 5),\n                            TestPageable(\"6\", 6)\n                    ),\n                    currentToken = ContinuationToken(timestamp = 6, offset = 1, checksum = checksum(\"6\"))\n            ))\n        }\n\n        @Test\n        fun `|1,3,3|3,3,6|`() {\n            val allEntries = listOf(\n                    TestPageable(\"1\", 1),\n                    TestPageable(\"2\", 3),\n                    TestPageable(\"3\", 3),\n                    TestPageable(\"4\", 3),\n                    TestPageable(\"5\", 3),\n                    TestPageable(\"6\", 6)\n            )\n            val firstPage = allEntries.getEntriesSinceIncluding(timestamp = 0, limit = 3)\n\n            val page = Pagination.createPage(firstPage, null, 3)\n            assertThat(page).isEqualTo(Page(\n                    entities = listOf(\n                            TestPageable(\"1\", 1),\n                            TestPageable(\"2\", 3),\n                            TestPageable(\"3\", 3)\n                    ),\n                    currentToken = ContinuationToken(timestamp = 3, offset = 2, checksum = checksum(\"2\", \"3\"))\n            ))\n\n            val entriesSinceKey = allEntries.getEntriesSinceIncluding(timestamp = 3, limit = 5)\n            val page2 = Pagination.createPage(entriesSinceKey, page.currentToken, 3)\n            assertThat(page2).isEqualTo(Page(\n                    entities = listOf(\n                            TestPageable(\"4\", 3),\n                            TestPageable(\"5\", 3),\n                            TestPageable(\"6\", 6)\n                    ),\n                    currentToken = ContinuationToken(timestamp = 6, offset = 1, checksum = checksum(\"6\"))\n            ))\n        }\n\n        @Test\n        fun `|1,2,3|3,3,6|`() {\n            val allEntries = listOf(\n                    TestPageable(\"1\", 1),\n                    TestPageable(\"2\", 2),\n                    TestPageable(\"3\", 3),\n                    TestPageable(\"4\", 3),\n                    TestPageable(\"5\", 3),\n                    TestPageable(\"6\", 6)\n            )\n            val firstPage = allEntries.getEntriesSinceIncluding(timestamp = 0, limit = 3)\n\n            val page = Pagination.createPage(firstPage, null, 3)\n            assertThat(page).isEqualTo(Page(\n                    entities = listOf(\n                            TestPageable(\"1\", 1),\n                            TestPageable(\"2\", 2),\n                            TestPageable(\"3\", 3)\n                    ),\n                    currentToken = ContinuationToken(timestamp = 3, offset = 1, checksum = checksum(\"3\"))\n            ))\n\n            val entriesSinceKey = allEntries.getEntriesSinceIncluding(timestamp = 3, limit = 5)\n            val page2 = Pagination.createPage(entriesSinceKey, page.currentToken, 3)\n            assertThat(page2).isEqualTo(Page(\n                    entities = listOf(\n                            TestPageable(\"4\", 3),\n                            TestPageable(\"5\", 3),\n                            TestPageable(\"6\", 6)\n                    ),\n                    currentToken = ContinuationToken(timestamp = 6, offset = 1, checksum = checksum(\"6\"))\n            ))\n        }\n\n        @Test\n        fun `|1,2,3|4,4,6|`() {\n            val allEntries = listOf(\n                    TestPageable(\"1\", 1),\n                    TestPageable(\"2\", 2),\n                    TestPageable(\"3\", 3),\n                    TestPageable(\"4\", 4),\n                    TestPageable(\"5\", 4),\n                    TestPageable(\"6\", 6)\n            )\n            val firstPage = allEntries.getEntriesSinceIncluding(timestamp = 0, limit = 3)\n\n            val page = Pagination.createPage(firstPage, null, 3)\n            assertThat(page).isEqualTo(Page(\n                    entities = listOf(\n                            TestPageable(\"1\", 1),\n                            TestPageable(\"2\", 2),\n                            TestPageable(\"3\", 3)\n                    ),\n                    currentToken = ContinuationToken(timestamp = 3, offset = 1, checksum = checksum(\"3\"))\n            ))\n\n            val entriesSinceKey = allEntries.getEntriesSinceIncluding(timestamp = 3, limit = 5)\n            val page2 = Pagination.createPage(entriesSinceKey, page.currentToken, 3)\n            assertThat(page2).isEqualTo(Page(\n                    entities = listOf(\n                            TestPageable(\"4\", 4),\n                            TestPageable(\"5\", 4),\n                            TestPageable(\"6\", 6)\n                    ),\n                    currentToken = ContinuationToken(timestamp = 6, offset = 1, checksum = checksum(\"6\"))\n            ))\n        }\n\n        @Test\n        fun `|1,1,1|1,1,2|`() {\n            val allEntries = listOf(\n                    TestPageable(\"1\", 1),\n                    TestPageable(\"2\", 1),\n                    TestPageable(\"3\", 1),\n                    TestPageable(\"4\", 1),\n                    TestPageable(\"5\", 1),\n                    TestPageable(\"6\", 2)\n            )\n            val firstPage = allEntries.getEntriesSinceIncluding(timestamp = 0, limit = 3)\n\n            val page = Pagination.createPage(firstPage, null, 3)\n            assertThat(page).isEqualTo(Page(\n                    entities = listOf(\n                            TestPageable(\"1\", 1),\n                            TestPageable(\"2\", 1),\n                            TestPageable(\"3\", 1)\n                    ),\n                    currentToken = ContinuationToken(timestamp = 1, offset = 3, checksum = checksum(\"1\", \"2\", \"3\"))\n            ))\n\n            val entriesSinceKey = allEntries.getEntriesSinceIncluding(timestamp = 1, limit = 6)\n            val page2 = Pagination.createPage(entriesSinceKey, page.currentToken, 3)\n            assertThat(page2).isEqualTo(Page(\n                    entities = listOf(\n                            TestPageable(\"4\", 1),\n                            TestPageable(\"5\", 1),\n                            TestPageable(\"6\", 2)\n                    ),\n                    currentToken = ContinuationToken(timestamp = 2, offset = 1, checksum = checksum(\"6\"))\n            ))\n        }\n\n        private fun List<Pageable>.getEntriesSinceIncluding(timestamp: Int, limit: Int)\n                = this.filter { it.getTimestamp() >= timestamp }.take(limit)\n    }\n\n\n    @Nested\n    inner class `createToken` {\n        @Test\n        fun `only one entity with highest timestamp`() {\n            val pageables = listOf(\n                    TestPageable(1),\n                    TestPageable(2),\n                    TestPageable(3),\n                    TestPageable(4)\n            )\n            val token = Pagination.createTokenForPage(pageables, pageables, 4)\n            assertThat(token).isEqualTo(ContinuationToken(timestamp = 4, offset = 1, checksum = checksum(\"4\")))\n        }\n        @Test\n        fun `two entities with highest timestamp`() {\n            val pageables = listOf(\n                    TestPageable(1),\n                    TestPageable(2),\n                    TestPageable(\"3\", 3),\n                    TestPageable(\"4\", 3)\n            )\n            val token = Pagination.createTokenForPage(pageables, pageables, 4)\n            assertThat(token).isEqualTo(ContinuationToken(timestamp = 3, offset = 2, checksum = checksum(\"3\", \"4\")))\n        }\n        @Test\n        fun `all elements have same timestamp`() {\n            val pageables = listOf(\n                    TestPageable(\"1\",1),\n                    TestPageable(\"2\",1),\n                    TestPageable(\"3\",1)\n            )\n            val token = Pagination.createTokenForPage(pageables, pageables, 3)\n            assertThat(token).isEqualTo(ContinuationToken(timestamp = 1, offset = 3, checksum = checksum(\"1\", \"2\", \"3\")))\n        }\n        @Test\n        fun `one element list`() {\n            val pageables = listOf(\n                    TestPageable(1)\n            )\n            val token = Pagination.createTokenForPage(pageables, pageables, 1)\n            assertThat(token).isEqualTo(ContinuationToken(timestamp = 1, offset = 1, checksum = checksum(\"1\")))\n        }\n        @Test\n        fun `empty list`() {\n            val token = Pagination.createTokenForPage(listOf(), listOf(), 10)\n            assertThat(token).isNull()\n        }\n        //TODO test varying pagesize!\n    }\n\n    @Nested\n    inner class `calculateQueryAdvice`{\n        @Test\n        fun `no token provided`(){\n            val advice = Pagination.calculateQueryAdvice(token = null, pageSize = 5)\n            assertThat(advice).isEqualTo(QueryAdvice(timestamp = 0, limit = 5))\n        }\n        @Test\n        fun `there was one element with timestamp 20 in the last page`(){\n            val token = ContinuationToken(timestamp = 20, offset = 1, checksum = 999)\n            val advice = Pagination.calculateQueryAdvice(token, pageSize = 5)\n            assertThat(advice).isEqualTo(QueryAdvice(timestamp = 20, limit = 6))\n        }\n        @Test\n        fun `there were 3 elements with timestamp 20 in the last page`(){\n            val token = ContinuationToken(timestamp = 20, offset = 3, checksum = 999)\n            val advice = Pagination.calculateQueryAdvice(token, pageSize = 5)\n            assertThat(advice).isEqualTo(QueryAdvice(timestamp = 20, limit = 8))\n        }\n    }\n\n    @Nested\n    inner class `getEntitiesWithHighestKey`{\n        @Test\n        fun `all have different keys`(){\n            val pageables = listOf(\n                    TestPageable(1),\n                    TestPageable(2),\n                    TestPageable(3)\n            )\n            val entities = Pagination.getEntitiesWithHighestTimestamp(pageables)\n            assertThat(entities).containsExactly(TestPageable(3))\n        }\n        @Test\n        fun `some with the same key`(){\n            val pageables = listOf(\n                    TestPageable(1),\n                    TestPageable(2),\n                    TestPageable(\"4\",3),\n                    TestPageable(\"5\",3)\n                    )\n            val entities = Pagination.getEntitiesWithHighestTimestamp(pageables)\n            assertThat(entities).containsExactly(TestPageable(\"4\",3), TestPageable(\"5\",3))\n        }\n        @Test\n        fun `all with the same key`(){\n            val pageables = listOf(\n                    TestPageable(\"1\",1),\n                    TestPageable(\"2\",1),\n                    TestPageable(\"3\",1)\n            )\n            val entities = Pagination.getEntitiesWithHighestTimestamp(pageables)\n            assertThat(entities).containsExactly(TestPageable(\"1\",1), TestPageable(\"2\",1), TestPageable(\"3\",1))\n        }\n        @Test\n        fun `empty list`(){\n            val entities = Pagination.getEntitiesWithHighestTimestamp(listOf())\n            assertThat(entities).isEmpty()\n        }\n        @Test\n        fun `only one element`(){\n            val pageables = listOf(TestPageable(1))\n            val entities = Pagination.getEntitiesWithHighestTimestamp(pageables)\n            assertThat(entities).containsExactly(TestPageable(\"1\",1))\n        }\n    }\n}\n\nprivate fun checksum(vararg ids: String): Long{\n    val hash = CRC32()\n    hash.update(ids.joinToString(\"_\").toByteArray())\n    return hash.value\n}\n\ndata class TestPageable(\n        private val id: String,\n        private val timestamp: Long\n): Pageable {\n    constructor(timestamp: Long): this(timestamp.toString(), timestamp)\n    override fun getID() = id\n    override fun getTimestamp() = timestamp\n}"
  },
  {
    "path": "continuation-token/demo-kotlin/.gitignore",
    "content": "target/\n!.mvn/wrapper/maven-wrapper.jar\n.idea/\n*.iml\ndependency-reduced-pom.xml"
  },
  {
    "path": "continuation-token/demo-kotlin/.mvn/wrapper/maven-wrapper.properties",
    "content": "distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.2/apache-maven-3.5.2-bin.zip\n"
  },
  {
    "path": "continuation-token/demo-kotlin/mvnw",
    "content": "#!/bin/sh\n# ----------------------------------------------------------------------------\n# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\n# ----------------------------------------------------------------------------\n\n# ----------------------------------------------------------------------------\n# Maven2 Start Up Batch script\n#\n# Required ENV vars:\n# ------------------\n#   JAVA_HOME - location of a JDK home dir\n#\n# Optional ENV vars\n# -----------------\n#   M2_HOME - location of maven2's installed home dir\n#   MAVEN_OPTS - parameters passed to the Java VM when running Maven\n#     e.g. to debug Maven itself, use\n#       set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000\n#   MAVEN_SKIP_RC - flag to disable loading of mavenrc files\n# ----------------------------------------------------------------------------\n\nif [ -z \"$MAVEN_SKIP_RC\" ] ; then\n\n  if [ -f /etc/mavenrc ] ; then\n    . /etc/mavenrc\n  fi\n\n  if [ -f \"$HOME/.mavenrc\" ] ; then\n    . \"$HOME/.mavenrc\"\n  fi\n\nfi\n\n# OS specific support.  $var _must_ be set to either true or false.\ncygwin=false;\ndarwin=false;\nmingw=false\ncase \"`uname`\" in\n  CYGWIN*) cygwin=true ;;\n  MINGW*) mingw=true;;\n  Darwin*) darwin=true\n    # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home\n    # See https://developer.apple.com/library/mac/qa/qa1170/_index.html\n    if [ -z \"$JAVA_HOME\" ]; then\n      if [ -x \"/usr/libexec/java_home\" ]; then\n        export JAVA_HOME=\"`/usr/libexec/java_home`\"\n      else\n        export JAVA_HOME=\"/Library/Java/Home\"\n      fi\n    fi\n    ;;\nesac\n\nif [ -z \"$JAVA_HOME\" ] ; then\n  if [ -r /etc/gentoo-release ] ; then\n    JAVA_HOME=`java-config --jre-home`\n  fi\nfi\n\nif [ -z \"$M2_HOME\" ] ; then\n  ## resolve links - $0 may be a link to maven's home\n  PRG=\"$0\"\n\n  # need this for relative symlinks\n  while [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n      PRG=\"$link\"\n    else\n      PRG=\"`dirname \"$PRG\"`/$link\"\n    fi\n  done\n\n  saveddir=`pwd`\n\n  M2_HOME=`dirname \"$PRG\"`/..\n\n  # make it fully qualified\n  M2_HOME=`cd \"$M2_HOME\" && pwd`\n\n  cd \"$saveddir\"\n  # echo Using m2 at $M2_HOME\nfi\n\n# For Cygwin, ensure paths are in UNIX format before anything is touched\nif $cygwin ; then\n  [ -n \"$M2_HOME\" ] &&\n    M2_HOME=`cygpath --unix \"$M2_HOME\"`\n  [ -n \"$JAVA_HOME\" ] &&\n    JAVA_HOME=`cygpath --unix \"$JAVA_HOME\"`\n  [ -n \"$CLASSPATH\" ] &&\n    CLASSPATH=`cygpath --path --unix \"$CLASSPATH\"`\nfi\n\n# For Migwn, ensure paths are in UNIX format before anything is touched\nif $mingw ; then\n  [ -n \"$M2_HOME\" ] &&\n    M2_HOME=\"`(cd \"$M2_HOME\"; pwd)`\"\n  [ -n \"$JAVA_HOME\" ] &&\n    JAVA_HOME=\"`(cd \"$JAVA_HOME\"; pwd)`\"\n  # TODO classpath?\nfi\n\nif [ -z \"$JAVA_HOME\" ]; then\n  javaExecutable=\"`which javac`\"\n  if [ -n \"$javaExecutable\" ] && ! [ \"`expr \\\"$javaExecutable\\\" : '\\([^ ]*\\)'`\" = \"no\" ]; then\n    # readlink(1) is not available as standard on Solaris 10.\n    readLink=`which readlink`\n    if [ ! `expr \"$readLink\" : '\\([^ ]*\\)'` = \"no\" ]; then\n      if $darwin ; then\n        javaHome=\"`dirname \\\"$javaExecutable\\\"`\"\n        javaExecutable=\"`cd \\\"$javaHome\\\" && pwd -P`/javac\"\n      else\n        javaExecutable=\"`readlink -f \\\"$javaExecutable\\\"`\"\n      fi\n      javaHome=\"`dirname \\\"$javaExecutable\\\"`\"\n      javaHome=`expr \"$javaHome\" : '\\(.*\\)/bin'`\n      JAVA_HOME=\"$javaHome\"\n      export JAVA_HOME\n    fi\n  fi\nfi\n\nif [ -z \"$JAVACMD\" ] ; then\n  if [ -n \"$JAVA_HOME\"  ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n      # IBM's JDK on AIX uses strange locations for the executables\n      JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n      JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n  else\n    JAVACMD=\"`which java`\"\n  fi\nfi\n\nif [ ! -x \"$JAVACMD\" ] ; then\n  echo \"Error: JAVA_HOME is not defined correctly.\" >&2\n  echo \"  We cannot execute $JAVACMD\" >&2\n  exit 1\nfi\n\nif [ -z \"$JAVA_HOME\" ] ; then\n  echo \"Warning: JAVA_HOME environment variable is not set.\"\nfi\n\nCLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher\n\n# traverses directory structure from process work directory to filesystem root\n# first directory with .mvn subdirectory is considered project base directory\nfind_maven_basedir() {\n\n  if [ -z \"$1\" ]\n  then\n    echo \"Path not specified to find_maven_basedir\"\n    return 1\n  fi\n\n  basedir=\"$1\"\n  wdir=\"$1\"\n  while [ \"$wdir\" != '/' ] ; do\n    if [ -d \"$wdir\"/.mvn ] ; then\n      basedir=$wdir\n      break\n    fi\n    # workaround for JBEAP-8937 (on Solaris 10/Sparc)\n    if [ -d \"${wdir}\" ]; then\n      wdir=`cd \"$wdir/..\"; pwd`\n    fi\n    # end of workaround\n  done\n  echo \"${basedir}\"\n}\n\n# concatenates all lines of a file\nconcat_lines() {\n  if [ -f \"$1\" ]; then\n    echo \"$(tr -s '\\n' ' ' < \"$1\")\"\n  fi\n}\n\nBASE_DIR=`find_maven_basedir \"$(pwd)\"`\nif [ -z \"$BASE_DIR\" ]; then\n  exit 1;\nfi\n\nexport MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-\"$BASE_DIR\"}\necho $MAVEN_PROJECTBASEDIR\nMAVEN_OPTS=\"$(concat_lines \"$MAVEN_PROJECTBASEDIR/.mvn/jvm.config\") $MAVEN_OPTS\"\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin; then\n  [ -n \"$M2_HOME\" ] &&\n    M2_HOME=`cygpath --path --windows \"$M2_HOME\"`\n  [ -n \"$JAVA_HOME\" ] &&\n    JAVA_HOME=`cygpath --path --windows \"$JAVA_HOME\"`\n  [ -n \"$CLASSPATH\" ] &&\n    CLASSPATH=`cygpath --path --windows \"$CLASSPATH\"`\n  [ -n \"$MAVEN_PROJECTBASEDIR\" ] &&\n    MAVEN_PROJECTBASEDIR=`cygpath --path --windows \"$MAVEN_PROJECTBASEDIR\"`\nfi\n\nWRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain\n\nexec \"$JAVACMD\" \\\n  $MAVEN_OPTS \\\n  -classpath \"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar\" \\\n  \"-Dmaven.home=${M2_HOME}\" \"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}\" \\\n  ${WRAPPER_LAUNCHER} $MAVEN_CONFIG \"$@\"\n"
  },
  {
    "path": "continuation-token/demo-kotlin/mvnw.cmd",
    "content": "@REM ----------------------------------------------------------------------------\n@REM Licensed to the Apache Software Foundation (ASF) under one\n@REM or more contributor license agreements.  See the NOTICE file\n@REM distributed with this work for additional information\n@REM regarding copyright ownership.  The ASF licenses this file\n@REM to you under the Apache License, Version 2.0 (the\n@REM \"License\"); you may not use this file except in compliance\n@REM with the License.  You may obtain a copy of the License at\n@REM\n@REM    http://www.apache.org/licenses/LICENSE-2.0\n@REM\n@REM Unless required by applicable law or agreed to in writing,\n@REM software distributed under the License is distributed on an\n@REM \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n@REM KIND, either express or implied.  See the License for the\n@REM specific language governing permissions and limitations\n@REM under the License.\n@REM ----------------------------------------------------------------------------\n\n@REM ----------------------------------------------------------------------------\n@REM Maven2 Start Up Batch script\n@REM\n@REM Required ENV vars:\n@REM JAVA_HOME - location of a JDK home dir\n@REM\n@REM Optional ENV vars\n@REM M2_HOME - location of maven2's installed home dir\n@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands\n@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending\n@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven\n@REM     e.g. to debug Maven itself, use\n@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000\n@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files\n@REM ----------------------------------------------------------------------------\n\n@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'\n@echo off\n@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'\n@if \"%MAVEN_BATCH_ECHO%\" == \"on\"  echo %MAVEN_BATCH_ECHO%\n\n@REM set %HOME% to equivalent of $HOME\nif \"%HOME%\" == \"\" (set \"HOME=%HOMEDRIVE%%HOMEPATH%\")\n\n@REM Execute a user defined script before this one\nif not \"%MAVEN_SKIP_RC%\" == \"\" goto skipRcPre\n@REM check for pre script, once with legacy .bat ending and once with .cmd ending\nif exist \"%HOME%\\mavenrc_pre.bat\" call \"%HOME%\\mavenrc_pre.bat\"\nif exist \"%HOME%\\mavenrc_pre.cmd\" call \"%HOME%\\mavenrc_pre.cmd\"\n:skipRcPre\n\n@setlocal\n\nset ERROR_CODE=0\n\n@REM To isolate internal variables from possible post scripts, we use another setlocal\n@setlocal\n\n@REM ==== START VALIDATION ====\nif not \"%JAVA_HOME%\" == \"\" goto OkJHome\n\necho.\necho Error: JAVA_HOME not found in your environment. >&2\necho Please set the JAVA_HOME variable in your environment to match the >&2\necho location of your Java installation. >&2\necho.\ngoto error\n\n:OkJHome\nif exist \"%JAVA_HOME%\\bin\\java.exe\" goto init\n\necho.\necho Error: JAVA_HOME is set to an invalid directory. >&2\necho JAVA_HOME = \"%JAVA_HOME%\" >&2\necho Please set the JAVA_HOME variable in your environment to match the >&2\necho location of your Java installation. >&2\necho.\ngoto error\n\n@REM ==== END VALIDATION ====\n\n:init\n\n@REM Find the project base dir, i.e. the directory that contains the folder \".mvn\".\n@REM Fallback to current working directory if not found.\n\nset MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%\nIF NOT \"%MAVEN_PROJECTBASEDIR%\"==\"\" goto endDetectBaseDir\n\nset EXEC_DIR=%CD%\nset WDIR=%EXEC_DIR%\n:findBaseDir\nIF EXIST \"%WDIR%\"\\.mvn goto baseDirFound\ncd ..\nIF \"%WDIR%\"==\"%CD%\" goto baseDirNotFound\nset WDIR=%CD%\ngoto findBaseDir\n\n:baseDirFound\nset MAVEN_PROJECTBASEDIR=%WDIR%\ncd \"%EXEC_DIR%\"\ngoto endDetectBaseDir\n\n:baseDirNotFound\nset MAVEN_PROJECTBASEDIR=%EXEC_DIR%\ncd \"%EXEC_DIR%\"\n\n:endDetectBaseDir\n\nIF NOT EXIST \"%MAVEN_PROJECTBASEDIR%\\.mvn\\jvm.config\" goto endReadAdditionalConfig\n\n@setlocal EnableExtensions EnableDelayedExpansion\nfor /F \"usebackq delims=\" %%a in (\"%MAVEN_PROJECTBASEDIR%\\.mvn\\jvm.config\") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a\n@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%\n\n:endReadAdditionalConfig\n\nSET MAVEN_JAVA_EXE=\"%JAVA_HOME%\\bin\\java.exe\"\n\nset WRAPPER_JAR=\"%MAVEN_PROJECTBASEDIR%\\.mvn\\wrapper\\maven-wrapper.jar\"\nset WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain\n\n%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% \"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%\" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*\nif ERRORLEVEL 1 goto error\ngoto end\n\n:error\nset ERROR_CODE=1\n\n:end\n@endlocal & set ERROR_CODE=%ERROR_CODE%\n\nif not \"%MAVEN_SKIP_RC%\" == \"\" goto skipRcPost\n@REM check for post script, once with legacy .bat ending and once with .cmd ending\nif exist \"%HOME%\\mavenrc_post.bat\" call \"%HOME%\\mavenrc_post.bat\"\nif exist \"%HOME%\\mavenrc_post.cmd\" call \"%HOME%\\mavenrc_post.cmd\"\n:skipRcPost\n\n@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'\nif \"%MAVEN_BATCH_PAUSE%\" == \"on\" pause\n\nif \"%MAVEN_TERMINATE_CMD%\" == \"on\" exit %ERROR_CODE%\n\nexit /B %ERROR_CODE%\n"
  },
  {
    "path": "continuation-token/demo-kotlin/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>de.philipphauer.blog</groupId>\n    <artifactId>demo-kotlin</artifactId>\n    <version>1.0-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <kotlin.version>1.1.60</kotlin.version>\n        <junit5.version>5.0.2</junit5.version>\n        <http4k.version>3.0.1</http4k.version>\n        <continuation-token.version>1.0.0-SNAPSHOT</continuation-token.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>de.philipphauer.blog</groupId>\n            <artifactId>continuation-token</artifactId>\n            <version>${continuation-token.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.jetbrains.kotlin</groupId>\n            <artifactId>kotlin-stdlib</artifactId>\n            <version>${kotlin.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.http4k</groupId>\n            <artifactId>http4k-core</artifactId>\n            <version>${http4k.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.http4k</groupId>\n            <artifactId>http4k-server-jetty</artifactId>\n            <version>${http4k.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.http4k</groupId>\n            <artifactId>http4k-format-jackson</artifactId>\n            <version>${http4k.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-jdbc</artifactId>\n            <version>5.0.1.RELEASE</version>\n        </dependency>\n        <dependency>\n            <groupId>com.h2database</groupId>\n            <artifactId>h2</artifactId>\n            <version>1.4.196</version>\n        </dependency>\n\n\n        <!-- test -->\n        <dependency>\n            <groupId>org.junit.jupiter</groupId>\n            <artifactId>junit-jupiter-api</artifactId>\n            <version>${junit5.version}</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.assertj</groupId>\n            <artifactId>assertj-core</artifactId>\n            <version>3.8.0</version>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <sourceDirectory>src/main/kotlin</sourceDirectory>\n        <testSourceDirectory>src/test/kotlin</testSourceDirectory>\n\n        <plugins>\n            <plugin>\n                <groupId>org.jetbrains.kotlin</groupId>\n                <artifactId>kotlin-maven-plugin</artifactId>\n                <version>${kotlin.version}</version>\n                <executions>\n                    <execution>\n                        <id>compile</id>\n                        <phase>compile</phase>\n                        <goals>\n                            <goal>compile</goal>\n                        </goals>\n                    </execution>\n                    <execution>\n                        <id>test-compile</id>\n                        <phase>test-compile</phase>\n                        <goals>\n                            <goal>test-compile</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <artifactId>maven-surefire-plugin</artifactId>\n                <version>2.19</version>\n                <dependencies>\n                    <dependency>\n                        <groupId>org.junit.platform</groupId>\n                        <artifactId>junit-platform-surefire-provider</artifactId>\n                        <version>1.0.2</version>\n                    </dependency>\n                    <dependency>\n                        <groupId>org.junit.jupiter</groupId>\n                        <artifactId>junit-jupiter-engine</artifactId>\n                        <version>${junit5.version}</version>\n                    </dependency>\n                </dependencies>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-shade-plugin</artifactId>\n                <version>3.1.0</version>\n                <configuration>\n                    <transformers>\n                        <transformer implementation=\"org.apache.maven.plugins.shade.resource.ManifestResourceTransformer\">\n                            <mainClass>de.philipphauer.blog.MainKt</mainClass>\n                        </transformer>\n                    </transformers>\n                </configuration>\n                <executions>\n                    <execution>\n                        <phase>package</phase>\n                        <goals>\n                            <goal>shade</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "continuation-token/demo-kotlin/src/main/kotlin/de/philipphauer/blog/DesignDAO.kt",
    "content": "package de.philipphauer.blog\n\nimport de.philipphauer.blog.pagination.ContinuationToken\nimport de.philipphauer.blog.pagination.Pagination\nimport org.springframework.jdbc.core.JdbcTemplate\nimport java.sql.ResultSet\nimport javax.sql.DataSource\n\n\nclass DesignDAO(dataSource: DataSource){\n\n    private val template = JdbcTemplate(dataSource)\n\n    fun getDesigns(token: ContinuationToken?, pageSize: Int): DesignPageEntity {\n        val queryAdvice = Pagination.calculateQueryAdvice(token, pageSize)\n        val sql = \"\"\"SELECT * FROM designs\n            WHERE UNIX_TIMESTAMP(dateModified) >= ${queryAdvice.timestamp}\n            ORDER BY dateModified asc, id asc\n            LIMIT ${queryAdvice.limit};\"\"\"\n        val designs = template.query(sql, this::mapToDesign)\n        val nextPage = Pagination.createPage(designs, token, pageSize)\n        return DesignPageEntity(nextPage.entities as List<DesignEntity>, nextPage.currentToken)\n    }\n\n    private fun mapToDesign(rs: ResultSet, rowNum: Int) = DesignEntity(\n            id = rs.getString(\"id\"),\n            title = rs.getString(\"title\"),\n            imageUrl = rs.getString(\"imageUrl\"),\n            dateModified = rs.getTimestamp(\"dateModified\").toInstant()\n    )\n}\n\ndata class DesignPageEntity(\n        val designs: List<DesignEntity>,\n        val token: ContinuationToken?\n)\n"
  },
  {
    "path": "continuation-token/demo-kotlin/src/main/kotlin/de/philipphauer/blog/DesignEntity.kt",
    "content": "package de.philipphauer.blog\n\nimport de.philipphauer.blog.pagination.Pageable\nimport java.time.Instant\n\ndata class DesignEntity(\n        val id: String,\n        val title: String,\n        val imageUrl: String,\n        val dateModified: Instant\n): Pageable {\n    override fun getID() = id\n    override fun getTimestamp() = dateModified.epochSecond\n}"
  },
  {
    "path": "continuation-token/demo-kotlin/src/main/kotlin/de/philipphauer/blog/DesignResource.kt",
    "content": "package de.philipphauer.blog\n\nimport com.fasterxml.jackson.module.kotlin.jacksonObjectMapper\nimport de.philipphauer.blog.pagination.ContinuationTokenParser\nimport org.http4k.core.Request\nimport org.http4k.core.Response\nimport org.http4k.core.Status\n\nclass DesignResource(val dao: DesignDAO) {\n\n    //TODO no next page on last page\n    //TODO checksum\n\n    fun getDesigns(request: Request): Response {\n        val token = request.query(\"continue\")?.let {ContinuationTokenParser.parse(it)}\n        val pageSize = request.query(\"pageSize\")?.toInt() ?: 100\n        val daoResult = dao.getDesigns(token, pageSize)\n        val dto = PageDTO(\n                results = daoResult.designs.map(::mapToDTO),\n                nextPage = if (daoResult.token == null) null else \"http://localhost:8000/designs?pageSize=$pageSize&continue=${daoResult.token}\"\n        )\n        return Response(Status.OK)\n                .header(\"Content-Type\", \"application/json;charset=UTF-8\")\n                .body(dto.toJson())\n    }\n}\n\nprivate fun mapToDTO(entity: DesignEntity) = DesignDTO(\n        id = entity.id,\n        title = entity.title,\n        imageUrl = entity.imageUrl,\n        dateModified = entity.dateModified.epochSecond\n)\n\ndata class DesignDTO(\n        val id: String,\n        val title: String,\n        val imageUrl: String,\n        val dateModified: Long\n)\ndata class PageDTO(\n        val results: List<DesignDTO>,\n        val nextPage: String?\n)\n\nprivate val mapper = jacksonObjectMapper()\nprivate fun Any.toJson() = mapper.writeValueAsString(this)\n\n"
  },
  {
    "path": "continuation-token/demo-kotlin/src/main/kotlin/de/philipphauer/blog/Main.kt",
    "content": "package de.philipphauer.blog\n\nimport de.philipphauer.blog.util.DesignCreator\nimport de.philipphauer.blog.util.FunctionsMySQL\nimport org.eclipse.jetty.server.NCSARequestLog\nimport org.eclipse.jetty.server.Server\nimport org.h2.jdbcx.JdbcDataSource\nimport org.http4k.core.Method\nimport org.http4k.routing.bind\nimport org.http4k.routing.routes\nimport org.http4k.server.Jetty\nimport org.http4k.server.asServer\nimport org.springframework.core.io.ClassPathResource\nimport org.springframework.jdbc.datasource.init.ScriptUtils\n\nfun main(args: Array<String>) {\n    val resource = bootstrapDesignResource()\n\n    val routingHandler = routes(\n            \"/designs\" bind Method.GET to resource::getDesigns\n    )\n    val jetty = Server(8000).apply {\n        requestLog = NCSARequestLog()\n    }\n    val server = routingHandler.asServer(Jetty(jetty)).start()\n    server.block()\n}\n\nprivate fun bootstrapDesignResource(): DesignResource {\n    val dataSource = JdbcDataSource().apply {\n        user = \"sa\"\n        password = \"\"\n        setURL(\"jdbc:h2:mem:access;MODE=MySQL;DB_CLOSE_DELAY=-1\")\n    }\n    FunctionsMySQL.register(dataSource.connection)\n    ScriptUtils.executeSqlScript(dataSource.connection,  ClassPathResource(\"create-designs-table.sql\"))\n\n    DesignCreator(dataSource).createDesigns(amount = 20)\n\n    val dao = DesignDAO(dataSource)\n    return DesignResource(dao)\n}\n\n"
  },
  {
    "path": "continuation-token/demo-kotlin/src/main/kotlin/de/philipphauer/blog/util/DesignCreator.kt",
    "content": "package de.philipphauer.blog.util\n\nimport org.springframework.jdbc.core.JdbcTemplate\nimport java.time.Instant\nimport javax.sql.DataSource\n\nclass DesignCreator(dataSource: DataSource) {\n\n    private val utilTemplate = JdbcTemplate(dataSource)\n\n    fun createDesigns(amount: Int) {\n        val now = Instant.now()\n        val values = (1..amount).mapIndexed{ i, _ -> arrayOf(\n                i,\n                \"Cat $i\",\n                \"http://domain.de/cat$i.jpg\",\n                now.plusSeconds(i.toLong()).epochSecond\n        ) }\n        utilTemplate.batchUpdate(\"INSERT INTO designs (id, title, imageUrl, dateModified) VALUES (?, ?, ?, FROM_UNIXTIME(?))\", values)\n    }\n}"
  },
  {
    "path": "continuation-token/demo-kotlin/src/main/kotlin/de/philipphauer/blog/util/FunctionsMySQL.kt",
    "content": "package de.philipphauer.blog.util\n\nimport org.h2.util.StringUtils\nimport java.sql.Connection\nimport java.sql.SQLException\nimport java.text.SimpleDateFormat\nimport java.util.Date\nimport java.util.Locale\n\n/**\n * https://github.com/h2database/h2database/blob/master/h2/src/main/org/h2/mode/FunctionsMySQL.java\n * This class implements some MySQL-specific functions.\n *\n * @author Jason Brittain\n * @author Thomas Mueller\n */\nobject FunctionsMySQL {\n\n    /**\n     * The date format of a MySQL formatted date/time.\n     * Example: 2008-09-25 08:40:59\n     */\n    private val DATE_TIME_FORMAT = \"yyyy-MM-dd HH:mm:ss\"\n\n    /**\n     * Format replacements for MySQL date formats.\n     * See\n     * http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_date-format\n     */\n    private val FORMAT_REPLACE = arrayOf(\"%a\", \"EEE\", \"%b\", \"MMM\", \"%c\", \"MM\", \"%d\", \"dd\", \"%e\", \"d\", \"%H\", \"HH\", \"%h\", \"hh\", \"%I\", \"hh\", \"%i\", \"mm\", \"%j\", \"DDD\", \"%k\", \"H\", \"%l\", \"h\", \"%M\", \"MMMM\", \"%m\", \"MM\", \"%p\", \"a\", \"%r\", \"hh:mm:ss a\", \"%S\", \"ss\", \"%s\", \"ss\", \"%T\", \"HH:mm:ss\", \"%W\", \"EEEE\", \"%w\", \"F\", \"%Y\", \"yyyy\", \"%y\", \"yy\", \"%%\", \"%\")\n\n    /**\n     * Register the functionality in the database.\n     * Nothing happens if the functions are already registered.\n     *\n     * @param conn the connection\n     */\n    @Throws(SQLException::class)\n    fun register(conn: Connection) {\n        val init = arrayOf(\"UNIX_TIMESTAMP\", \"unixTimestamp\", \"FROM_UNIXTIME\", \"fromUnixTime\", \"DATE\", \"date\")\n        val stat = conn.createStatement()\n        var i = 0\n        while (i < init.size) {\n            val alias = init[i]\n            val method = init[i + 1]\n            stat.execute(\n                    \"CREATE ALIAS IF NOT EXISTS \" + alias +\n                            \" FOR \\\"\" + FunctionsMySQL::class.java!!.name + \".\" + method + \"\\\"\")\n            i += 2\n        }\n    }\n\n    /**\n     * Get the seconds since 1970-01-01 00:00:00 UTC.\n     * See\n     * http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_unix-timestamp\n     *\n     * @return the current timestamp in seconds (not milliseconds).\n     */\n    @JvmStatic\n    fun unixTimestamp(): Int {\n        return (System.currentTimeMillis() / 1000L).toInt()\n    }\n\n    /**\n     * Get the seconds since 1970-01-01 00:00:00 UTC of the given timestamp.\n     * See\n     * http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_unix-timestamp\n     *\n     * @param timestamp the timestamp\n     * @return the current timestamp in seconds (not milliseconds).\n     */\n    @JvmStatic\n    fun unixTimestamp(timestamp: java.sql.Timestamp): Int {\n        return (timestamp.time / 1000L).toInt()\n    }\n\n    /**\n     * See\n     * http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_from-unixtime\n     *\n     * @param seconds The current timestamp in seconds.\n     * @return a formatted date/time String in the format \"yyyy-MM-dd HH:mm:ss\".\n     */\n    @JvmStatic\n    fun fromUnixTime(seconds: Int): String {\n        val formatter = SimpleDateFormat(DATE_TIME_FORMAT,\n                Locale.ENGLISH)\n        return formatter.format(Date(seconds * 1000L))\n    }\n\n    /**\n     * See\n     * http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_from-unixtime\n     *\n     * @param seconds The current timestamp in seconds.\n     * @param format The format of the date/time String to return.\n     * @return a formatted date/time String in the given format.\n     */\n    @JvmStatic\n    fun fromUnixTime(seconds: Int, format: String): String {\n        var format = format\n        format = convertToSimpleDateFormat(format)\n        val formatter = SimpleDateFormat(format, Locale.ENGLISH)\n        return formatter.format(Date(seconds * 1000L))\n    }\n\n    private fun convertToSimpleDateFormat(format: String): String {\n        var format = format\n        val replace = FORMAT_REPLACE\n        var i = 0\n        while (i < replace.size) {\n            format = StringUtils.replaceAll(format, replace[i], replace[i + 1])\n            i += 2\n        }\n        return format\n    }\n\n    /**\n     * See\n     * http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_date\n     * This function is dependent on the exact formatting of the MySQL date/time\n     * string.\n     *\n     * @param dateTime The date/time String from which to extract just the date\n     * part.\n     * @return the date part of the given date/time String argument.\n     */\n    @JvmStatic\n    fun date(dateTime: String?): String? {\n        if (dateTime == null) {\n            return null\n        }\n        val index = dateTime!!.indexOf(' ')\n        return if (index != -1) {\n            dateTime!!.substring(0, index)\n        } else dateTime\n    }\n\n}"
  },
  {
    "path": "continuation-token/demo-kotlin/src/main/resources/create-designs-table.sql",
    "content": "CREATE TABLE designs (\n  id int AUTO_INCREMENT PRIMARY KEY,\n  title varchar(100) NOT NULL,\n  imageUrl varchar(100) NOT NULL,\n  dateModified TIMESTAMP NOT NULL\n);"
  },
  {
    "path": "continuation-token/demo-kotlin/src/test/kotlin/de/philipphauer/blog/DesignResourceTest.kt",
    "content": "package de.philipphauer.blog\n\nimport com.fasterxml.jackson.databind.DeserializationFeature\nimport com.fasterxml.jackson.module.kotlin.jacksonObjectMapper\nimport de.philipphauer.blog.util.DesignCreator\nimport de.philipphauer.blog.util.FunctionsMySQL\nimport org.h2.jdbcx.JdbcDataSource\nimport org.http4k.core.Method\nimport org.http4k.core.Request\nimport org.http4k.core.Response\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.TestInstance\nimport org.springframework.core.io.ClassPathResource\nimport org.springframework.jdbc.datasource.init.ScriptUtils\n\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\ninternal class DesignResourceTest {\n\n    private val resource = initDesignResource()\n    private val creator = DesignCreator(dataSource)\n\n    @Test\n    fun `happy path`() {\n        creator.createDesigns(amount = 10)\n        val response = resource.getDesigns(Request(Method.GET, \"/designs?pageSize=3\"))\n\n        println(response)\n//        assertThat(response.toPageable().nextPage).contains(\"?continue=TODO\")\n    }\n\n    //TODO test: find better test abstraction. e.g. PaginationTest\n    // page with same key/ts\n    // final page (amount < page size and = page size)\n    // first element of next page: a) is first one with new key, b) not the same one\n    // empty result\n    // correct page size\n    // no `nextPage` in last page\n    // checksum usage\n\n\n    private fun initDesignResource(): DesignResource {\n        val dao = DesignDAO(dataSource)\n        FunctionsMySQL.register(dataSource.connection)\n        ScriptUtils.executeSqlScript(dataSource.connection,  ClassPathResource(\"create-designs-table.sql\"))\n        return DesignResource(dao)\n    }\n}\n\nprivate val dataSource = JdbcDataSource().apply {\n    user = \"sa\"\n    password = \"\"\n    setURL(\"jdbc:h2:mem:access;MODE=MySQL;DB_CLOSE_DELAY=-1\")\n}\n\nprivate val mapper = jacksonObjectMapper().apply {\n    configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)\n}\nprivate fun Response.toPageable() = mapper.readValue(bodyString(), PageableResponse::class.java)\n\ndata class PageableResponse(\n        val nextPage: String\n)\n"
  },
  {
    "path": "development-productivity-vaadin-spring-boot/.gitignore",
    "content": "target/\n!.mvn/wrapper/maven-wrapper.jar\n\n### STS ###\n.apt_generated\n.classpath\n.factorypath\n.project\n.settings\n.springBeans\n\n### IntelliJ IDEA ###\n.idea\n*.iws\n*.iml\n*.ipr\n\n### NetBeans ###\nnbproject/private/\nbuild/\nnbbuild/\ndist/\nnbdist/\n.nb-gradle/\n\nsrc/main/webapp/VAADIN/themes/mytheme/styles.css\nsrc/main/webapp/VAADIN/themes/mytheme/styles.scss.cache\n\nsrc/main/resources/VAADIN/themes/mytheme/styles.css"
  },
  {
    "path": "development-productivity-vaadin-spring-boot/.mvn/wrapper/maven-wrapper.properties",
    "content": "distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip\n"
  },
  {
    "path": "development-productivity-vaadin-spring-boot/Makefile",
    "content": ".PHONY: all watch\n\nSHELL:=/bin/bash\nBROWSERSYNC:=/usr/local/bin/browser-sync\nall: watch\n\nwatch: $(BROWSERSYNC)\n\tbrowser-sync start --proxy 'localhost:8080' --files 'src/main/webapp/VAADIN/themes/mytheme/*.scss'\n\n$(BROWSERSYNC):\n\tsudo npm install -g browser-sync\n"
  },
  {
    "path": "development-productivity-vaadin-spring-boot/README.md",
    "content": "An example application with some bootstrapping (spring security, JDBC auto configuration, h2 console, actuator endpoints, vaadin view discovery) to keep the application busy during startup. this way, we can better see the impact of our optimizations.\n\nBuild and Run\n\n```bash\n./mvnw clean package && java -jar target/development-productivity-vaadin-spring-boot*.jar\n```\n\nSearch in created jar\n\n```bash\n./mvnw clean package\nunzip -l target/development-productivity-vaadin-spring-boot*.jar | grep css \n```\n\nCompile Vaadin Theme\n\n```bash\n./mvnw vaadin:compile-theme \n```\n\nCheck out `src/main/resources/application.properties`.\n\n# Side Notes\n\nWhere to put `VAADIN/themes/mytheme`? `src/main/resources` or `src/main/webapp`? Vaadin's on-the-fly compilation works in both cases! (given: there is no `styles.css` (`src` and `target/classes`) and `production-mode=false`)\n\n- `webapp`: sass changes are automatically detected and a recompilation is triggered. `styles.scss.cache` is created. no manual action required. But somehow, there is no `styles.css` in the built jar, although everything works fine.\n- `resources`: after a change (in let's say `mytheme.scss`), you have to hit `Ctrl+Shift+F9` in IDEA. Now, the change is detected and recompilation is triggered. There is no `styles.scss.cache` at all.\n  - Advantages: it's the [recommend location](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-web-applications.html#boot-features-spring-mvc-static-content) of spring boot! Moreover, you can find the generated styles.css in the jar!\n  - Drawback: more uncomfortable theme editing.\n  \nIf somebody can explain this behavior, please approach me!\n"
  },
  {
    "path": "development-productivity-vaadin-spring-boot/mvnw",
    "content": "#!/bin/sh\n# ----------------------------------------------------------------------------\n# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\n# ----------------------------------------------------------------------------\n\n# ----------------------------------------------------------------------------\n# Maven2 Start Up Batch script\n#\n# Required ENV vars:\n# ------------------\n#   JAVA_HOME - location of a JDK home dir\n#\n# Optional ENV vars\n# -----------------\n#   M2_HOME - location of maven2's installed home dir\n#   MAVEN_OPTS - parameters passed to the Java VM when running Maven\n#     e.g. to debug Maven itself, use\n#       set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000\n#   MAVEN_SKIP_RC - flag to disable loading of mavenrc files\n# ----------------------------------------------------------------------------\n\nif [ -z \"$MAVEN_SKIP_RC\" ] ; then\n\n  if [ -f /etc/mavenrc ] ; then\n    . /etc/mavenrc\n  fi\n\n  if [ -f \"$HOME/.mavenrc\" ] ; then\n    . \"$HOME/.mavenrc\"\n  fi\n\nfi\n\n# OS specific support.  $var _must_ be set to either true or false.\ncygwin=false;\ndarwin=false;\nmingw=false\ncase \"`uname`\" in\n  CYGWIN*) cygwin=true ;;\n  MINGW*) mingw=true;;\n  Darwin*) darwin=true\n    # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home\n    # See https://developer.apple.com/library/mac/qa/qa1170/_index.html\n    if [ -z \"$JAVA_HOME\" ]; then\n      if [ -x \"/usr/libexec/java_home\" ]; then\n        export JAVA_HOME=\"`/usr/libexec/java_home`\"\n      else\n        export JAVA_HOME=\"/Library/Java/Home\"\n      fi\n    fi\n    ;;\nesac\n\nif [ -z \"$JAVA_HOME\" ] ; then\n  if [ -r /etc/gentoo-release ] ; then\n    JAVA_HOME=`java-config --jre-home`\n  fi\nfi\n\nif [ -z \"$M2_HOME\" ] ; then\n  ## resolve links - $0 may be a link to maven's home\n  PRG=\"$0\"\n\n  # need this for relative symlinks\n  while [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n      PRG=\"$link\"\n    else\n      PRG=\"`dirname \"$PRG\"`/$link\"\n    fi\n  done\n\n  saveddir=`pwd`\n\n  M2_HOME=`dirname \"$PRG\"`/..\n\n  # make it fully qualified\n  M2_HOME=`cd \"$M2_HOME\" && pwd`\n\n  cd \"$saveddir\"\n  # echo Using m2 at $M2_HOME\nfi\n\n# For Cygwin, ensure paths are in UNIX format before anything is touched\nif $cygwin ; then\n  [ -n \"$M2_HOME\" ] &&\n    M2_HOME=`cygpath --unix \"$M2_HOME\"`\n  [ -n \"$JAVA_HOME\" ] &&\n    JAVA_HOME=`cygpath --unix \"$JAVA_HOME\"`\n  [ -n \"$CLASSPATH\" ] &&\n    CLASSPATH=`cygpath --path --unix \"$CLASSPATH\"`\nfi\n\n# For Migwn, ensure paths are in UNIX format before anything is touched\nif $mingw ; then\n  [ -n \"$M2_HOME\" ] &&\n    M2_HOME=\"`(cd \"$M2_HOME\"; pwd)`\"\n  [ -n \"$JAVA_HOME\" ] &&\n    JAVA_HOME=\"`(cd \"$JAVA_HOME\"; pwd)`\"\n  # TODO classpath?\nfi\n\nif [ -z \"$JAVA_HOME\" ]; then\n  javaExecutable=\"`which javac`\"\n  if [ -n \"$javaExecutable\" ] && ! [ \"`expr \\\"$javaExecutable\\\" : '\\([^ ]*\\)'`\" = \"no\" ]; then\n    # readlink(1) is not available as standard on Solaris 10.\n    readLink=`which readlink`\n    if [ ! `expr \"$readLink\" : '\\([^ ]*\\)'` = \"no\" ]; then\n      if $darwin ; then\n        javaHome=\"`dirname \\\"$javaExecutable\\\"`\"\n        javaExecutable=\"`cd \\\"$javaHome\\\" && pwd -P`/javac\"\n      else\n        javaExecutable=\"`readlink -f \\\"$javaExecutable\\\"`\"\n      fi\n      javaHome=\"`dirname \\\"$javaExecutable\\\"`\"\n      javaHome=`expr \"$javaHome\" : '\\(.*\\)/bin'`\n      JAVA_HOME=\"$javaHome\"\n      export JAVA_HOME\n    fi\n  fi\nfi\n\nif [ -z \"$JAVACMD\" ] ; then\n  if [ -n \"$JAVA_HOME\"  ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n      # IBM's JDK on AIX uses strange locations for the executables\n      JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n      JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n  else\n    JAVACMD=\"`which java`\"\n  fi\nfi\n\nif [ ! -x \"$JAVACMD\" ] ; then\n  echo \"Error: JAVA_HOME is not defined correctly.\" >&2\n  echo \"  We cannot execute $JAVACMD\" >&2\n  exit 1\nfi\n\nif [ -z \"$JAVA_HOME\" ] ; then\n  echo \"Warning: JAVA_HOME environment variable is not set.\"\nfi\n\nCLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher\n\n# traverses directory structure from process work directory to filesystem root\n# first directory with .mvn subdirectory is considered project base directory\nfind_maven_basedir() {\n\n  if [ -z \"$1\" ]\n  then\n    echo \"Path not specified to find_maven_basedir\"\n    return 1\n  fi\n\n  basedir=\"$1\"\n  wdir=\"$1\"\n  while [ \"$wdir\" != '/' ] ; do\n    if [ -d \"$wdir\"/.mvn ] ; then\n      basedir=$wdir\n      break\n    fi\n    # workaround for JBEAP-8937 (on Solaris 10/Sparc)\n    if [ -d \"${wdir}\" ]; then\n      wdir=`cd \"$wdir/..\"; pwd`\n    fi\n    # end of workaround\n  done\n  echo \"${basedir}\"\n}\n\n# concatenates all lines of a file\nconcat_lines() {\n  if [ -f \"$1\" ]; then\n    echo \"$(tr -s '\\n' ' ' < \"$1\")\"\n  fi\n}\n\nBASE_DIR=`find_maven_basedir \"$(pwd)\"`\nif [ -z \"$BASE_DIR\" ]; then\n  exit 1;\nfi\n\nexport MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-\"$BASE_DIR\"}\necho $MAVEN_PROJECTBASEDIR\nMAVEN_OPTS=\"$(concat_lines \"$MAVEN_PROJECTBASEDIR/.mvn/jvm.config\") $MAVEN_OPTS\"\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin; then\n  [ -n \"$M2_HOME\" ] &&\n    M2_HOME=`cygpath --path --windows \"$M2_HOME\"`\n  [ -n \"$JAVA_HOME\" ] &&\n    JAVA_HOME=`cygpath --path --windows \"$JAVA_HOME\"`\n  [ -n \"$CLASSPATH\" ] &&\n    CLASSPATH=`cygpath --path --windows \"$CLASSPATH\"`\n  [ -n \"$MAVEN_PROJECTBASEDIR\" ] &&\n    MAVEN_PROJECTBASEDIR=`cygpath --path --windows \"$MAVEN_PROJECTBASEDIR\"`\nfi\n\nWRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain\n\nexec \"$JAVACMD\" \\\n  $MAVEN_OPTS \\\n  -classpath \"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar\" \\\n  \"-Dmaven.home=${M2_HOME}\" \"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}\" \\\n  ${WRAPPER_LAUNCHER} $MAVEN_CONFIG \"$@\"\n"
  },
  {
    "path": "development-productivity-vaadin-spring-boot/mvnw.cmd",
    "content": "@REM ----------------------------------------------------------------------------\n@REM Licensed to the Apache Software Foundation (ASF) under one\n@REM or more contributor license agreements.  See the NOTICE file\n@REM distributed with this work for additional information\n@REM regarding copyright ownership.  The ASF licenses this file\n@REM to you under the Apache License, Version 2.0 (the\n@REM \"License\"); you may not use this file except in compliance\n@REM with the License.  You may obtain a copy of the License at\n@REM\n@REM    http://www.apache.org/licenses/LICENSE-2.0\n@REM\n@REM Unless required by applicable law or agreed to in writing,\n@REM software distributed under the License is distributed on an\n@REM \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n@REM KIND, either express or implied.  See the License for the\n@REM specific language governing permissions and limitations\n@REM under the License.\n@REM ----------------------------------------------------------------------------\n\n@REM ----------------------------------------------------------------------------\n@REM Maven2 Start Up Batch script\n@REM\n@REM Required ENV vars:\n@REM JAVA_HOME - location of a JDK home dir\n@REM\n@REM Optional ENV vars\n@REM M2_HOME - location of maven2's installed home dir\n@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands\n@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending\n@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven\n@REM     e.g. to debug Maven itself, use\n@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000\n@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files\n@REM ----------------------------------------------------------------------------\n\n@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'\n@echo off\n@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'\n@if \"%MAVEN_BATCH_ECHO%\" == \"on\"  echo %MAVEN_BATCH_ECHO%\n\n@REM set %HOME% to equivalent of $HOME\nif \"%HOME%\" == \"\" (set \"HOME=%HOMEDRIVE%%HOMEPATH%\")\n\n@REM Execute a user defined script before this one\nif not \"%MAVEN_SKIP_RC%\" == \"\" goto skipRcPre\n@REM check for pre script, once with legacy .bat ending and once with .cmd ending\nif exist \"%HOME%\\mavenrc_pre.bat\" call \"%HOME%\\mavenrc_pre.bat\"\nif exist \"%HOME%\\mavenrc_pre.cmd\" call \"%HOME%\\mavenrc_pre.cmd\"\n:skipRcPre\n\n@setlocal\n\nset ERROR_CODE=0\n\n@REM To isolate internal variables from possible post scripts, we use another setlocal\n@setlocal\n\n@REM ==== START VALIDATION ====\nif not \"%JAVA_HOME%\" == \"\" goto OkJHome\n\necho.\necho Error: JAVA_HOME not found in your environment. >&2\necho Please set the JAVA_HOME variable in your environment to match the >&2\necho location of your Java installation. >&2\necho.\ngoto error\n\n:OkJHome\nif exist \"%JAVA_HOME%\\bin\\java.exe\" goto init\n\necho.\necho Error: JAVA_HOME is set to an invalid directory. >&2\necho JAVA_HOME = \"%JAVA_HOME%\" >&2\necho Please set the JAVA_HOME variable in your environment to match the >&2\necho location of your Java installation. >&2\necho.\ngoto error\n\n@REM ==== END VALIDATION ====\n\n:init\n\n@REM Find the project base dir, i.e. the directory that contains the folder \".mvn\".\n@REM Fallback to current working directory if not found.\n\nset MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%\nIF NOT \"%MAVEN_PROJECTBASEDIR%\"==\"\" goto endDetectBaseDir\n\nset EXEC_DIR=%CD%\nset WDIR=%EXEC_DIR%\n:findBaseDir\nIF EXIST \"%WDIR%\"\\.mvn goto baseDirFound\ncd ..\nIF \"%WDIR%\"==\"%CD%\" goto baseDirNotFound\nset WDIR=%CD%\ngoto findBaseDir\n\n:baseDirFound\nset MAVEN_PROJECTBASEDIR=%WDIR%\ncd \"%EXEC_DIR%\"\ngoto endDetectBaseDir\n\n:baseDirNotFound\nset MAVEN_PROJECTBASEDIR=%EXEC_DIR%\ncd \"%EXEC_DIR%\"\n\n:endDetectBaseDir\n\nIF NOT EXIST \"%MAVEN_PROJECTBASEDIR%\\.mvn\\jvm.config\" goto endReadAdditionalConfig\n\n@setlocal EnableExtensions EnableDelayedExpansion\nfor /F \"usebackq delims=\" %%a in (\"%MAVEN_PROJECTBASEDIR%\\.mvn\\jvm.config\") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a\n@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%\n\n:endReadAdditionalConfig\n\nSET MAVEN_JAVA_EXE=\"%JAVA_HOME%\\bin\\java.exe\"\n\nset WRAPPER_JAR=\"%MAVEN_PROJECTBASEDIR%\\.mvn\\wrapper\\maven-wrapper.jar\"\nset WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain\n\n%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% \"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%\" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*\nif ERRORLEVEL 1 goto error\ngoto end\n\n:error\nset ERROR_CODE=1\n\n:end\n@endlocal & set ERROR_CODE=%ERROR_CODE%\n\nif not \"%MAVEN_SKIP_RC%\" == \"\" goto skipRcPost\n@REM check for post script, once with legacy .bat ending and once with .cmd ending\nif exist \"%HOME%\\mavenrc_post.bat\" call \"%HOME%\\mavenrc_post.bat\"\nif exist \"%HOME%\\mavenrc_post.cmd\" call \"%HOME%\\mavenrc_post.cmd\"\n:skipRcPost\n\n@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'\nif \"%MAVEN_BATCH_PAUSE%\" == \"on\" pause\n\nif \"%MAVEN_TERMINATE_CMD%\" == \"on\" exit %ERROR_CODE%\n\nexit /B %ERROR_CODE%\n"
  },
  {
    "path": "development-productivity-vaadin-spring-boot/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>de.philipphauer.blog</groupId>\n\t<artifactId>development-productivity-vaadin-spring-boot</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>development-productivity-vaadin-spring-boot</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.7.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.8</java.version>\n\t\t<vaadin.version>8.1.0</vaadin.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-actuator</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-actuator-docs</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-mustache</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-security</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.vaadin</groupId>\n\t\t\t<artifactId>vaadin-spring-boot-starter</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-jdbc</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.h2database</groupId>\n\t\t\t<artifactId>h2</artifactId>\n\t\t\t<version>1.4.196</version>\n\t\t</dependency>\n\n\t\t<!-- restart and livereload are disabled in application.properties -->\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-devtools</artifactId>\n\t\t\t<optional>true</optional>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.security</groupId>\n\t\t\t<artifactId>spring-security-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>com.vaadin</groupId>\n\t\t\t\t<artifactId>vaadin-bom</artifactId>\n\t\t\t\t<version>${vaadin.version}</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t\t<plugin>\n\t\t\t\t<groupId>com.vaadin</groupId>\n\t\t\t\t<artifactId>vaadin-maven-plugin</artifactId>\n\t\t\t\t<version>${vaadin.version}</version>\n\t\t\t\t<executions>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>resources</goal>\n\t\t\t\t\t\t\t<goal>update-theme</goal>\n\t\t\t\t\t\t\t<goal>compile-theme</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "development-productivity-vaadin-spring-boot/src/main/java/de/philipphauer/blog/devproductivity/DevProductivityApplication.java",
    "content": "package de.philipphauer.blog.devproductivity;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DevProductivityApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(DevProductivityApplication.class, args);\n\t}\n}\n"
  },
  {
    "path": "development-productivity-vaadin-spring-boot/src/main/java/de/philipphauer/blog/devproductivity/WebSecurityConfiguration.java",
    "content": "package de.philipphauer.blog.devproductivity;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.builders.WebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\n\n@Configuration\nclass WebSecurityConfiguration extends WebSecurityConfigurerAdapter {\n    @Override\n    protected void configure(HttpSecurity http) throws Exception {\n        http.csrf().disable();\n        http.authorizeRequests().anyRequest().permitAll();\n        http.httpBasic().disable();\n        http.formLogin().disable();\n    }\n}"
  },
  {
    "path": "development-productivity-vaadin-spring-boot/src/main/java/de/philipphauer/blog/devproductivity/model/Role.java",
    "content": "package de.philipphauer.blog.devproductivity.model;\n\npublic enum Role {\n    GUEST, REGISTERED_USER\n}\n"
  },
  {
    "path": "development-productivity-vaadin-spring-boot/src/main/java/de/philipphauer/blog/devproductivity/model/User.java",
    "content": "package de.philipphauer.blog.devproductivity.model;\n\npublic class User {\n    private int id;\n    private String firstName;\n    private String lastName;\n    private int age;\n    private Role role;\n    private boolean active;\n\n    public int getId() {\n        return id;\n    }\n\n    public User setId(int id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getFirstName() {\n        return firstName;\n    }\n\n    public User setFirstName(String firstName) {\n        this.firstName = firstName;\n        return this;\n    }\n\n    public String getLastName() {\n        return lastName;\n    }\n\n    public User setLastName(String lastName) {\n        this.lastName = lastName;\n        return this;\n    }\n\n    public int getAge() {\n        return age;\n    }\n\n    public User setAge(int age) {\n        this.age = age;\n        return this;\n    }\n\n    public Role getRole() {\n        return role;\n    }\n\n    public User setRole(Role role) {\n        this.role = role;\n        return this;\n    }\n\n    public boolean isActive() {\n        return active;\n    }\n\n    public User setActive(boolean active) {\n        this.active = active;\n        return this;\n    }\n}\n"
  },
  {
    "path": "development-productivity-vaadin-spring-boot/src/main/java/de/philipphauer/blog/devproductivity/rest/AdminResource.java",
    "content": "package de.philipphauer.blog.devproductivity.rest;\n\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.GetMapping;\n\n@Controller\npublic class AdminResource {\n\n    @GetMapping(\"/\")\n    public String redirectToUI(){\n        return \"redirect:/ui\";\n    }\n\n    @GetMapping(\"favicon.ico\")\n    public String favicon(){\n        return \"forward:/VAADIN/themes/sqljunkie/favicon.ico\";\n    }\n\n    @GetMapping(\"/customResource\")\n    public String customResource(){\n        return \"Hi!\";\n    }\n}\n"
  },
  {
    "path": "development-productivity-vaadin-spring-boot/src/main/java/de/philipphauer/blog/devproductivity/ui/MyAppUI.java",
    "content": "package de.philipphauer.blog.devproductivity.ui;\n\nimport com.vaadin.annotations.Theme;\nimport com.vaadin.server.VaadinRequest;\nimport com.vaadin.spring.annotation.SpringUI;\nimport com.vaadin.ui.Grid;\nimport com.vaadin.ui.Label;\nimport com.vaadin.ui.UI;\nimport com.vaadin.ui.VerticalLayout;\nimport com.vaadin.ui.themes.ValoTheme;\nimport de.philipphauer.blog.devproductivity.model.Role;\nimport de.philipphauer.blog.devproductivity.model.User;\n\nimport java.util.List;\nimport java.util.Random;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n@SpringUI(path = \"\")\n@Theme(\"mytheme\")\npublic class MyAppUI extends UI {\n\n    private Grid<User> table = new Grid<>(User.class);\n    private Label heading = new Label(\"Development Productivity Demo\");\n\n    @Override\n    protected void init(VaadinRequest vaadinRequest) {\n        getPage().setTitle(\"Development Productivity Demo\");\n\n        table.setSizeFull();\n        table.setItems(generateDummyUsers());\n\n        heading.addStyleName(ValoTheme.LABEL_H1);\n\n        VerticalLayout layout = new VerticalLayout();\n        layout.addComponent(heading);\n        layout.addComponentsAndExpand(table);\n        setContent(layout);\n\n        System.out.println(\"Session Object ID:\"+getSession().hashCode());\n    }\n\n    private List<User> generateDummyUsers() {\n        Random random = new Random();\n        return Stream.generate(() -> random.nextInt(9999))\n                .limit(50)\n                .map(uuid -> new User()\n                        .setId(uuid)\n                        .setFirstName(\"Paul\")\n                        .setLastName(\"Stark\")\n                        .setActive(true)\n                        .setRole(Role.REGISTERED_USER)\n                        .setActive(true))\n                .collect(Collectors.toList());\n    }\n}\n"
  },
  {
    "path": "development-productivity-vaadin-spring-boot/src/main/resources/application.properties",
    "content": "# but I prefer to control this via program arguments in my IDE's run configuration\nvaadin.servlet.production-mode=false\n\n# https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-devtools.html\nspring.devtools.restart.enabled=false\nspring.devtools.livereload.enabled=false\n\n\n\n# just some stuff to keep our application busy during startup:\nspring.h2.console.enabled=true\nspring.h2.console.path=/h2\nspring.datasource.url=jdbc:h2:file:~/test\nspring.datasource.username=sa\nspring.datasource.password=\nspring.datasource.driver-class-name=org.h2.Driver\n\n# deactivate default favicon delivery. use my dedicated resource for this. this resources uses the favicon in the vaadin theme.\nspring.mvc.favicon.enabled=false\n"
  },
  {
    "path": "development-productivity-vaadin-spring-boot/src/main/webapp/VAADIN/themes/mytheme/addons.scss",
    "content": "/* This file is automatically managed and will be overwritten from time to time. */\n/* Do not manually edit this file. */\n\n/* Import and include this mixin into your project theme to include the addon themes */\n@mixin addons {\n}\n\n"
  },
  {
    "path": "development-productivity-vaadin-spring-boot/src/main/webapp/VAADIN/themes/mytheme/mytheme.scss",
    "content": "@import \"../valo/valo.scss\";\n\n@mixin mytheme {\n  @include valo;\n\n  .monospace {\n    font-family: monospace;\n  }\n\n  //div {\n  //  background-color: blue;\n  //}\n}"
  },
  {
    "path": "development-productivity-vaadin-spring-boot/src/main/webapp/VAADIN/themes/mytheme/styles.scss",
    "content": "@import \"addons\";\n@import \"mytheme\";\n\n.mytheme {\n  @include addons;\n  @include mytheme;\n}"
  },
  {
    "path": "dont-use-in-memory-databases-tests/db-container-managed-by-gradle/.gitignore",
    "content": ".gradle/\n.idea/\nout/\nbuild/\n*.iml"
  },
  {
    "path": "dont-use-in-memory-databases-tests/db-container-managed-by-gradle/build.gradle",
    "content": "plugins {\n    id \"com.chrisgahlert.gradle-dcompose-plugin\" version \"0.9.1\"\n    id 'org.springframework.boot' version '1.4.2.RELEASE'\n}\n\napply plugin: 'java'\n\ngroup 'de.philipphauer.blog'\nversion '1.0-SNAPSHOT'\nsourceCompatibility = 1.8\n\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    compile 'org.springframework.boot:spring-boot-starter-jdbc'\n    compile 'mysql:mysql-connector-java:6.0.5'\n    testCompile group: 'junit', name: 'junit', version: '4.12'\n}\n\ndef mysqlTestPort = 3306\ndef mysqlTestPw = 'root'\n\ndcompose {\n    database {\n        image = 'mysql:5.5.53'\n        portBindings = [\"$mysqlTestPort:3306\"]\n        env = [\"MYSQL_ROOT_PASSWORD=$mysqlTestPw\"]\n    }\n}\n\ntest {\n    dependsOn startDatabaseContainer\n    finalizedBy stopDatabaseContainer\n    doFirst {\n        systemProperty 'mysql.port', mysqlTestPort\n        systemProperty 'mysql.pw', mysqlTestPw\n    }\n}\n"
  },
  {
    "path": "dont-use-in-memory-databases-tests/db-container-managed-by-gradle/docker-compose.yml",
    "content": "version: '2'\nservices:\n  mysql:\n    image: mysql:5.5.53\n    ports:\n      - \"3306:3306\"\n    environment:\n      MYSQL_ROOT_PASSWORD: \"root\"\n      MYSQL_DATABASE: \"testdb\"\n"
  },
  {
    "path": "dont-use-in-memory-databases-tests/db-container-managed-by-gradle/gradle/wrapper/gradle-wrapper.properties",
    "content": "#Fri Aug 18 18:26:51 CEST 2017\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-4.1-all.zip\n"
  },
  {
    "path": "dont-use-in-memory-databases-tests/db-container-managed-by-gradle/gradlew",
    "content": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=$(save \"$@\")\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\n# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong\nif [ \"$(uname)\" = \"Darwin\" ] && [ \"$HOME\" = \"$PWD\" ]; then\n  cd \"$(dirname \"$0\")\"\nfi\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "dont-use-in-memory-databases-tests/db-container-managed-by-gradle/gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windows variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "dont-use-in-memory-databases-tests/db-container-managed-by-gradle/settings.gradle",
    "content": "rootProject.name = 'db-container-managed-by-gradle'\n\n"
  },
  {
    "path": "dont-use-in-memory-databases-tests/db-container-managed-by-gradle/src/test/java/de/philipphauer/blog/MyTest.java",
    "content": "package de.philipphauer.blog;\n\nimport org.junit.BeforeClass;\nimport org.junit.Test;\nimport org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.DataSourceTransactionManager;\nimport org.springframework.transaction.TransactionStatus;\nimport org.springframework.transaction.support.DefaultTransactionDefinition;\n\nimport javax.sql.DataSource;\n\npublic class MyTest {\n\n    private static DataSource dataSource;\n\n    @BeforeClass\n    public static void init() throws InterruptedException{\n        Thread.sleep(5_000); //wait for the docker container to start\n        String port = System.getProperty(\"mysql.port\", \"3306\");\n        String pw = System.getProperty(\"mysql.pw\", \"root\");\n        String url = \"jdbc:mysql://localhost:\" + port + \"?autoReconnect=true\";\n        System.out.println(\"MySQL URL: \" + url +\" with pw \" + pw);\n        dataSource = DataSourceBuilder.create()\n                .url(url)\n                .username(\"root\")\n                .password(pw)\n                .driverClassName(\"com.mysql.cj.jdbc.Driver\")\n                .build();\n    }\n\n    @Test\n    public void foo(){\n        DataSourceTransactionManager txManager = new DataSourceTransactionManager(dataSource);\n        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);\n        TransactionStatus transaction = txManager.getTransaction(new DefaultTransactionDefinition());\n        jdbcTemplate.execute(\"DROP SCHEMA IF EXISTS testdb\"); //dcompose reuses container\n        jdbcTemplate.execute(\"CREATE SCHEMA testdb\");\n        jdbcTemplate.execute(\"CREATE TABLE testdb.BAR (\" +\n                \"id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,\" +\n                \"name varchar(200)\" +\n                \");\");\n        txManager.commit(transaction);\n    }\n}\n"
  },
  {
    "path": "dont-use-in-memory-databases-tests/db-container-managed-by-maven/.gitignore",
    "content": ".idea/\ntarget/"
  },
  {
    "path": "dont-use-in-memory-databases-tests/db-container-managed-by-maven/docker-compose.yml",
    "content": "version: '2'\nservices:\n  mysql:\n    image: mysql:5.5.53\n    ports:\n      - \"3306:3306\"\n    environment:\n      MYSQL_ROOT_PASSWORD: \"root\"\n      MYSQL_DATABASE: \"testdb\"\n"
  },
  {
    "path": "dont-use-in-memory-databases-tests/db-container-managed-by-maven/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>de.philipphauer.blog</groupId>\n    <artifactId>db-container-managed-by-maven</artifactId>\n    <version>1.0-SNAPSHOT</version>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.4.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <mysql.test.port>3306</mysql.test.port>\n        <mysql.test.pw>root</mysql.test.pw>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>6.0.5</version>\n        </dependency>\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <version>4.12</version>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <version>3.5.1</version>\n                <configuration>\n                    <source>1.8</source>\n                    <target>1.8</target>\n                </configuration>\n            </plugin>\n            <plugin>\n                <groupId>io.fabric8</groupId>\n                <artifactId>docker-maven-plugin</artifactId>\n                <version>0.21.0</version>\n                <configuration>\n                    <images>\n                        <image>\n                            <name>mysql:5.5.53</name>\n                            <alias>mysql</alias>\n                            <run>\n                                <env>\n                                    <MYSQL_ROOT_PASSWORD>${mysql.test.pw}</MYSQL_ROOT_PASSWORD>\n                                </env>\n                                <ports>\n                                    <port>${mysql.test.port}:3306</port>\n                                </ports>\n                                <wait>\n                                    <!-- time based waiting is the only option, because TCP-based or log-based polling are not reliable.\n                                    see https://github.com/fabric8io/docker-maven-plugin/issues/328\n                                    Alternatively we can wait log- or tcp-based and try to connect for a while in the test code -->\n                                    <time>8000</time>\n                                </wait>\n                            </run>\n                        </image>\n                    </images>\n                </configuration>\n                <executions>\n                    <execution>\n                        <id>start</id>\n                        <phase>pre-integration-test</phase>\n                        <goals>\n                            <goal>start</goal>\n                        </goals>\n                    </execution>\n                    <execution>\n                        <id>stop</id>\n                        <phase>post-integration-test</phase>\n                        <goals>\n                            <goal>stop</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-failsafe-plugin</artifactId>\n                <version>2.18.1</version>\n                <configuration>\n                    <includes>\n                        <include>**/*IT.*</include>\n                    </includes>\n                    <systemPropertyVariables>\n                        <mysql.port>${mysql.test.port}</mysql.port>\n                        <mysql.pw>${mysql.test.pw}</mysql.pw>\n                    </systemPropertyVariables>\n                </configuration>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>integration-test</goal>\n                            <goal>verify</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-surefire-plugin</artifactId>\n                <version>2.18.1</version>\n                <configuration>\n                    <excludes>\n                        <exclude>**/*IT.*</exclude>\n                    </excludes>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>"
  },
  {
    "path": "dont-use-in-memory-databases-tests/db-container-managed-by-maven/src/test/java/de/philipphauer/blog/MyIT.java",
    "content": "package de.philipphauer.blog;\n\nimport org.junit.BeforeClass;\nimport org.junit.Test;\nimport org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.DataSourceTransactionManager;\nimport org.springframework.transaction.TransactionStatus;\nimport org.springframework.transaction.support.DefaultTransactionDefinition;\n\nimport javax.sql.DataSource;\n\npublic class MyIT {\n\n    private static DataSource dataSource;\n\n    @BeforeClass\n    public static void init() throws InterruptedException{\n        // the maven-docker-plugin already waits. no need to do it here.\n        String port = System.getProperty(\"mysql.port\", \"3306\");\n        String pw = System.getProperty(\"mysql.pw\", \"root\");\n        String url = \"jdbc:mysql://localhost:\" + port + \"?autoReconnect=true\";\n        System.out.println(\"MySQL URL: \" + url +\" with pw \" + pw);\n        dataSource = DataSourceBuilder.create()\n                .url(url)\n                .username(\"root\")\n                .password(pw)\n                .driverClassName(\"com.mysql.cj.jdbc.Driver\")\n                .build();\n    }\n\n    @Test\n    public void foo(){\n        DataSourceTransactionManager txManager = new DataSourceTransactionManager(dataSource);\n        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);\n        TransactionStatus transaction = txManager.getTransaction(new DefaultTransactionDefinition());\n        jdbcTemplate.execute(\"DROP SCHEMA IF EXISTS testdb\");\n        jdbcTemplate.execute(\"CREATE SCHEMA testdb\");\n        jdbcTemplate.execute(\"CREATE TABLE testdb.BAR (\" +\n                \"id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,\" +\n                \"name varchar(200)\" +\n                \");\");\n        txManager.commit(transaction);\n    }\n}\n"
  },
  {
    "path": "dont-use-in-memory-databases-tests/db-container-managed-by-the-test/.gitignore",
    "content": ".gradle/\n.idea/\nout/\nbuild/\n*.iml"
  },
  {
    "path": "dont-use-in-memory-databases-tests/db-container-managed-by-the-test/build.gradle",
    "content": "plugins {\n    id 'org.springframework.boot' version '1.4.2.RELEASE'\n}\n\napply plugin: 'java'\n\ngroup 'de.philipphauer.blog'\nversion '1.0-SNAPSHOT'\nsourceCompatibility = 1.8\n\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    compile 'org.springframework.boot:spring-boot-starter-jdbc'\n    compile 'mysql:mysql-connector-java:6.0.5'\n    testCompile group: 'junit', name: 'junit', version: '4.12'\n    testCompile('org.testcontainers:mysql:1.4.2')\n}\n"
  },
  {
    "path": "dont-use-in-memory-databases-tests/db-container-managed-by-the-test/docker-compose.yml",
    "content": "version: '2'\nservices:\n  mysql:\n    image: mysql:5.5.53\n    ports:\n      - \"3306:3306\"\n    environment:\n      MYSQL_ROOT_PASSWORD: \"root\"\n      MYSQL_DATABASE: \"testdb\"\n"
  },
  {
    "path": "dont-use-in-memory-databases-tests/db-container-managed-by-the-test/gradle/wrapper/gradle-wrapper.properties",
    "content": "#Fri Aug 18 18:26:51 CEST 2017\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-4.1-all.zip\n"
  },
  {
    "path": "dont-use-in-memory-databases-tests/db-container-managed-by-the-test/gradlew",
    "content": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=$(save \"$@\")\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\n# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong\nif [ \"$(uname)\" = \"Darwin\" ] && [ \"$HOME\" = \"$PWD\" ]; then\n  cd \"$(dirname \"$0\")\"\nfi\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "dont-use-in-memory-databases-tests/db-container-managed-by-the-test/gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windows variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "dont-use-in-memory-databases-tests/db-container-managed-by-the-test/settings.gradle",
    "content": "rootProject.name = 'db-container-managed-by-gradle'\n\n"
  },
  {
    "path": "dont-use-in-memory-databases-tests/db-container-managed-by-the-test/src/test/java/de/philipphauer/blog/MyTest.java",
    "content": "package de.philipphauer.blog;\n\nimport org.junit.AfterClass;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\nimport org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.DataSourceTransactionManager;\nimport org.springframework.transaction.TransactionStatus;\nimport org.springframework.transaction.support.DefaultTransactionDefinition;\nimport org.testcontainers.containers.MySQLContainer;\n\nimport javax.sql.DataSource;\n\npublic class MyTest {\n\n    private static DataSource dataSource;\n    private static MySQLContainer mysql;\n\n    @BeforeClass\n    public static void init() throws InterruptedException{\n        //You can also use the GenericContainer for arbitrary containers\n        //But there are convenient classes for common databases.\n        mysql = new MySQLContainer(\"mysql:5.5.53\");\n        mysql.start();\n        dataSource = DataSourceBuilder.create()\n                .url(mysql.getJdbcUrl())\n                .username(mysql.getUsername())\n                .password(mysql.getPassword())\n                .driverClassName(\"com.mysql.cj.jdbc.Driver\")\n                .build();\n    }\n\n    @AfterClass\n    public static void destroy(){\n        mysql.close();\n    }\n\n    @Test\n    public void foo(){\n        DataSourceTransactionManager txManager = new DataSourceTransactionManager(dataSource);\n        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);\n        TransactionStatus transaction = txManager.getTransaction(new DefaultTransactionDefinition());\n        jdbcTemplate.execute(\"CREATE TABLE BAR (\" +\n                \"id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,\" +\n                \"name varchar(200)\" +\n                \");\");\n        txManager.commit(transaction);\n    }\n}\n"
  },
  {
    "path": "framework-beats-generator/.gitignore",
    "content": "db\n\n# Created by .ignore support plugin (hsz.mobi)\n### Maven template\ntarget/\npom.xml.tag\npom.xml.releaseBackup\npom.xml.versionsBackup\npom.xml.next\nrelease.properties\ndependency-reduced-pom.xml\nbuildNumber.properties\n.mvn/timing.properties\n### Example user template template\n### Example user template\n\n# IntelliJ project files\n.idea\n*.iml\nout\ngen\n"
  },
  {
    "path": "framework-beats-generator/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>de.philipphauer</groupId>\n    <artifactId>framework-beats-generator</artifactId>\n    <version>1.0-SNAPSHOT</version>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.mongojack</groupId>\n            <artifactId>mongojack</artifactId>\n            <version>2.5.1</version>\n        </dependency>\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <version>4.12</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.h2database</groupId>\n            <artifactId>h2</artifactId>\n            <version>1.4.190</version>\n        </dependency>\n        <dependency>\n            <groupId>org.hibernate</groupId>\n            <artifactId>hibernate-entitymanager</artifactId>\n            <version>5.0.6.Final</version>\n        </dependency>\n        <dependency>\n            <groupId>javax.transaction</groupId>\n            <artifactId>javax.transaction-api</artifactId>\n            <version>1.2</version>\n        </dependency>\n        <dependency>\n            <groupId>org.assertj</groupId>\n            <artifactId>assertj-core</artifactId>\n            <version>3.2.0</version>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>com.github.joelittlejohn.embedmongo</groupId>\n                <artifactId>embedmongo-maven-plugin</artifactId>\n                <version>0.3.1</version>\n                <configuration>\n                    <version>3.2.0</version>\n                    <bindIp>127.0.0.1</bindIp>\n                    <wait>true</wait>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n</project>"
  },
  {
    "path": "framework-beats-generator/src/main/java/de/philipphauer/h2/H2WebConsole.java",
    "content": "package de.philipphauer.h2;\n\nimport org.h2.tools.Server;\n\nimport java.sql.SQLException;\n\npublic class H2WebConsole {\n\n    public static void start() {\n        try {\n            Server server = Server.createWebServer(new String[]{}).start();\n            System.out.println(\"H2 can be accessed in port: \"+server.getPort());\n            System.out.println(\"URL: jdbc:h2:./db/repository\");\n            System.out.println(\"Empty user and pw\");\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "framework-beats-generator/src/main/java/de/philipphauer/jpa/Article.java",
    "content": "package de.philipphauer.jpa;\n\nimport javax.persistence.Entity;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\n\n@Entity\npublic class Article {\n\n    @Id\n    @GeneratedValue\n    private int id;\n\n    private String name;\n\n    public Article(String name){\n        this.name = name;\n    }\n\n    public Article(){\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public int getId() {\n        return id;\n    }\n}\n"
  },
  {
    "path": "framework-beats-generator/src/main/java/de/philipphauer/jpa/ArticleDAO.java",
    "content": "package de.philipphauer.jpa;\n\nimport javax.persistence.EntityManager;\nimport javax.persistence.EntityManagerFactory;\nimport javax.persistence.Persistence;\nimport javax.persistence.Query;\nimport java.util.Collection;\n\npublic class ArticleDAO {\n\n    private final EntityManager entityManager;\n\n    public ArticleDAO() {\n        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(\"myPersistenceUnit\");\n        this.entityManager = entityManagerFactory.createEntityManager();\n    }\n\n    public void save(Article article) {\n        entityManager.getTransaction().begin();\n        entityManager.persist(article);\n        entityManager.getTransaction().commit();\n    }\n\n    public Collection<Article> findAll() {\n        Query query = entityManager.createQuery(\"SELECT e FROM Article e\");\n        return (Collection<Article>) query.getResultList();\n    }\n\n    public void close(){\n        entityManager.close();\n    }\n}\n"
  },
  {
    "path": "framework-beats-generator/src/main/java/de/philipphauer/mongojack/Product.java",
    "content": "package de.philipphauer.mongojack;\n\nimport org.mongojack.ObjectId;\n\npublic class Product {\n    @ObjectId\n    private String id;\n    private String name;\n    private int price;\n\n    public Product(String name, int price) {\n        this.name = name;\n        this.price = price;\n    }\n\n    public String getId() {\n        return id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public int getPrice() {\n        return price;\n    }\n}"
  },
  {
    "path": "framework-beats-generator/src/main/java/de/philipphauer/mongojack/ProductDAO.java",
    "content": "package de.philipphauer.mongojack;\n\nimport com.mongodb.DB;\nimport com.mongodb.DBCollection;\nimport com.mongodb.MongoClient;\nimport com.mongodb.ServerAddress;\nimport org.mongojack.JacksonDBCollection;\n\nimport java.net.UnknownHostException;\n\npublic class ProductDAO {\n\n    public void save(Product product) throws UnknownHostException {\n        MongoClient mongoClient = new MongoClient(new ServerAddress(\"localhost\", 27017));\n        DB db = mongoClient.getDB(\"test\");\n        DBCollection collection = db.getCollection(\"products\");\n        JacksonDBCollection<Product, String> productCollection = JacksonDBCollection.wrap(collection, Product.class,\n                String.class);\n        productCollection.insert(product);\n        mongoClient.close();\n    }\n}\n"
  },
  {
    "path": "framework-beats-generator/src/main/resources/META-INF/persistence.xml",
    "content": "<persistence xmlns=\"http://java.sun.com/xml/ns/persistence\"\n             xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n             xsi:schemaLocation=\"http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd\"\n             version=\"2.0\">\n    <persistence-unit name=\"myPersistenceUnit\" transaction-type=\"RESOURCE_LOCAL\">\n        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>\n        <properties>\n            <property name=\"connection.driver_class\" value=\"org.h2.Driver\"/>\n            <property name=\"hibernate.connection.url\" value=\"jdbc:h2:./db/repository\"/>\n            <property name=\"hibernate.dialect\" value=\"org.hibernate.dialect.H2Dialect\"/>\n            <property name=\"hibernate.hbm2ddl.auto\" value=\"create-drop\"/>\n            <property name=\"hibernate.show_sql\" value=\"true\" />\n            <property name=\"javax.persistence.jdbc.user\" value=\"\"/>\n            <property name=\"javax.persistence.jdbc.password\" value=\"\"/>\n        </properties>\n    </persistence-unit>\n</persistence>"
  },
  {
    "path": "framework-beats-generator/src/test/java/de/philipphauer/jpa/ArticleDAOTest.java",
    "content": "package de.philipphauer.jpa;\n\nimport de.philipphauer.h2.H2WebConsole;\nimport org.junit.Test;\n\npublic class ArticleDAOTest {\n\n    @Test\n    public void saveAndLoad() throws Exception {\n        ArticleDAO dao = new ArticleDAO();\n        Article article = new Article(\"Car\");\n        dao.save(article);\n\n//        Collection<Article> articles = dao.findAll();\n//        Assertions.assertThat(articles).contains(article);\n\n        System.out.println(\"Now you can take a look at the h2 web console...\");\n        H2WebConsole.start();\n        Thread.sleep(40000);\n\n    }\n}"
  },
  {
    "path": "framework-beats-generator/src/test/java/de/philipphauer/jpa/H2Test.java",
    "content": "package de.philipphauer.jpa;\n\nimport org.junit.Test;\n\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.SQLException;\n\npublic class H2Test {\n\n    @Test\n    public void connection() throws ClassNotFoundException, SQLException {\n        Class.forName(\"org.h2.Driver\");\n        Connection conn = DriverManager.getConnection(\"jdbc:h2:~/test\", \"sa\", \"\");\n        conn.close();\n    }\n}\n"
  },
  {
    "path": "framework-beats-generator/src/test/java/de/philipphauer/mongojack/ProductDAOTest.java",
    "content": "package de.philipphauer.mongojack;\n\nimport org.junit.Test;\n\npublic class ProductDAOTest {\n\n    @Test\n    public void save() throws Exception {\n        ProductDAO dao = new ProductDAO();\n        dao.save(new Product(\"Lego Car\", 100));\n    }\n}"
  },
  {
    "path": "framework-beats-generator/startMongoDBLocally.bat",
    "content": "rem local mongodb installation needed\n\necho \"Creating dbpath\"\nmkdir \\data\\db\\\n\necho \"Starting MongoDB...\"\nstart mongod \n\necho \"Starting MongoDB Client...\"\nstart mongo"
  },
  {
    "path": "kotlin-examples/.gitignore",
    "content": ".idea/\ntarget/\n*.iml"
  },
  {
    "path": "kotlin-examples/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>de.philipphauer.blog</groupId>\n    <artifactId>kotlin-examples</artifactId>\n    <version>1.0-SNAPSHOT</version>\n\n    <properties>\n        <kotlin.version>1.0.5-2</kotlin.version>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.jetbrains.kotlin</groupId>\n            <artifactId>kotlin-stdlib</artifactId>\n            <version>${kotlin.version}</version>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <artifactId>kotlin-maven-plugin</artifactId>\n                <groupId>org.jetbrains.kotlin</groupId>\n                <version>${kotlin.version}</version>\n                <executions>\n                    <execution>\n                        <id>compile</id>\n                        <goals>\n                            <goal>compile</goal>\n                        </goals>\n                        <configuration>\n                            <sourceDirs>\n                                <sourceDir>${project.basedir}/src/main/kotlin</sourceDir>\n                                <sourceDir>${project.basedir}/src/main/java</sourceDir>\n                            </sourceDirs>\n                        </configuration>\n                    </execution>\n                    <execution>\n                        <id>test-compile</id>\n                        <goals>\n                            <goal>test-compile</goal>\n                        </goals>\n                        <configuration>\n                            <sourceDirs>\n                                <sourceDir>${project.basedir}/src/main/kotlin</sourceDir>\n                                <sourceDir>${project.basedir}/src/main/java</sourceDir>\n                            </sourceDirs>\n                        </configuration>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <version>3.5.1</version>\n                <configuration>\n                    <source>${java.version}</source>\n                    <target>${java.version}</target>\n                </configuration>\n                <executions>\n                    <!-- Replacing default-compile as it is treated specially by maven -->\n                    <execution>\n                        <id>default-compile</id>\n                        <phase>none</phase>\n                    </execution>\n                    <!-- Replacing default-testCompile as it is treated specially by maven -->\n                    <execution>\n                        <id>default-testCompile</id>\n                        <phase>none</phase>\n                    </execution>\n                    <execution>\n                        <id>java-compile</id>\n                        <phase>compile</phase>\n                        <goals>\n                            <goal>compile</goal>\n                        </goals>\n                    </execution>\n                    <execution>\n                        <id>java-test-compile</id>\n                        <phase>test-compile</phase>\n                        <goals>\n                            <goal>testCompile</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>"
  },
  {
    "path": "kotlin-examples/src/main/java/javaVariant/1DefineAndMapBeans.java",
    "content": "package javaVariant;\n\nimport java.time.Instant;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n//BlogEntity (received from persistence layer) map to BlogDTO (returned by our REST Service)\n//Struct definition and mapping code are extremely verbose in java!\n//Besides, null handling is cumbersome. Especially when it comes to nested objects that can be null.\n//All values can be null. It's easy to run into NullPointerExceptions. This leads to error-prone code. And even if you add null-checks, it's easy to forget a check (because the compiler doesn't help you) and the code becomes very verbose.\n//Argument lists are hard to read and error prone\nclass BlogEntity {\n    private long id;\n    private String name;\n    private List<PostEntity> posts;\n\n    //Praise my IDE for generating the constructor and getter boilerplate. Otherwise I would drive nuts.\n    //Moreover, equals(), hashCode(), toString() are still missing!\n    //AND you have to maintain these methods when field are added or removed.\n    public BlogEntity(long id, String name, List<PostEntity> posts) {\n        this.id = id;\n        this.name = name;\n        this.posts = posts;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public List<PostEntity> getPosts() {\n        return posts;\n    }\n\n    public long getId() {\n        return id;\n    }\n}\n\nclass PostEntity {\n    private long id;\n    private AuthorEntity author;\n    private Instant date;\n    private String text;\n    private List<CommentEntity> comments;\n\n    public PostEntity(long id, AuthorEntity author, Instant date, String text, List<CommentEntity> comments) {\n        this.id = id;\n        this.author = author;\n        this.date = date;\n        this.text = text;\n        this.comments = comments;\n    }\n\n    public AuthorEntity getAuthor() {\n        return author;\n    }\n\n    public Instant getDate() {\n        return date;\n    }\n\n    public String getText() {\n        return text;\n    }\n\n    public List<CommentEntity> getComments() {\n        return comments;\n    }\n\n    public long getId() {\n        return id;\n    }\n}\n\nclass AuthorEntity {\n    private String name;\n    private String email;\n\n    public AuthorEntity(String name, String email) {\n        this.name = name;\n        this.email = email;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public String getEmail() {\n        return email;\n    }\n}\n\nclass CommentEntity {\n    private String text;\n    private AuthorEntity author;\n    private Instant date;\n\n    public CommentEntity(String text, AuthorEntity author, Instant date) {\n        this.text = text;\n        this.author = author;\n        this.date = date;\n    }\n\n    public String getText() {\n        return text;\n    }\n\n    public AuthorEntity getAuthor() {\n        return author;\n    }\n\n    public Instant getDate() {\n        return date;\n    }\n}\n\nclass BlogDTO{\n    private long id;\n    private String name;\n    private List<PostDTO> posts;\n\n    public BlogDTO(long id, String name, List<PostDTO> posts) {\n        this.id = id;\n        this.name = name;\n        this.posts = posts;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public List<PostDTO> getPosts() {\n        return posts;\n    }\n\n    public long getId() {\n        return id;\n    }\n\n}\nclass PostDTO{\n    private long id;\n    private String date;\n    private String author;\n    private String text;\n    private String commentsHref;\n\n    public PostDTO(long id, String date, String author, String text, String commentsHref) {\n        this.id = id;\n        this.date = date;\n        this.author = author;\n        this.text = text;\n        this.commentsHref = commentsHref;\n    }\n\n    public String getAuthor() {\n        return author;\n    }\n\n    public String getDate() {\n        return date;\n    }\n\n    public String getText() {\n        return text;\n    }\n\n    public String getCommentsHref() {\n        return commentsHref;\n    }\n\n    public long getId() {\n        return id;\n    }\n}\n\nclass Mapper {\n    public List<BlogDTO> mapToBlogDTOs(List<BlogEntity> entities){\n        //verbose stream api\n        return entities.stream()\n                .map(this::mapToBlogDTO)\n                .collect(Collectors.toList());\n    }\n    private BlogDTO mapToBlogDTO(BlogEntity entity){\n        return new BlogDTO(\n                entity.getId(),\n                entity.getName(),\n                mapToPostDTO(entity.getPosts())\n        );\n    }\n\n    private List<PostDTO> mapToPostDTO(List<PostEntity> posts) {\n        //what if posts is null?! very unsafe code!\n        return posts.stream()\n                .map(this::mapToPostDTO)\n                .collect(Collectors.toList());\n    }\n\n    private PostDTO mapToPostDTO(PostEntity post) {\n        //what if author, date, text or comments are null?! error-prone code!\n        //easy to mess up parameter order (most of them are strings). hard to understand meaning of last parameter.\n        return new PostDTO(\n                post.getId(),\n                post.getDate() != null ? post.getDate().getEpochSecond()+ \"\" : null, //hard to read. easy to forget null check.\n                getNameOrDefault(post.getAuthor()),\n                post.getText(),\n                \"posts/\" + post.getId() + \"/comments\"\n        );\n    }\n\n    //null checks bloat code. very verbose. hard to read.\n    private String getNameOrDefault(AuthorEntity author) {\n        if (author != null){\n            String name = author.getName();\n            if (name != null){\n                return name;\n            }\n        }\n        return \"Anonymous\";\n    }\n}\n"
  },
  {
    "path": "kotlin-examples/src/main/java/javaVariant/2ConditionsAndTypeSwitch.java",
    "content": "package javaVariant;\n\nimport java.sql.SQLException;\nimport java.util.Locale;\n\nclass Conditions {\n    public Locale getDefaultLocale(String deliveryArea){\n        if (deliveryArea.equals(\"germany\") || deliveryArea.equals(\"austria\") || deliveryArea.equals(\"switzerland\")) {\n            return Locale.GERMAN;\n        }\n        if (deliveryArea.equals(\"usa\") || deliveryArea.equals(\"great britain\") || deliveryArea.equals(\"australia\")) {\n            return Locale.ENGLISH;\n        }\n        throw new IllegalArgumentException(\"Unsupported deliveryArea \" + deliveryArea);\n    }\n    //or via switch and fall-through\n\n\n    //verbose. annoying type cast.\n    public String getExceptionMessage(Exception exception){\n        if (exception instanceof MyLabeledException){\n            return ((MyLabeledException) exception).getLabel();\n        } else if (exception instanceof SQLException){\n            return exception.getMessage() + \". state: \" + ((SQLException) exception).getSQLState();\n        } else {\n            return exception.getMessage();\n        }\n    }\n}\n\nclass MyLabeledException extends RuntimeException{\n    private String label;\n\n    public String getLabel() {\n        return label;\n    }\n\n    public void setLabel(String label) {\n        this.label = label;\n    }\n}"
  },
  {
    "path": "kotlin-examples/src/main/java/kotlinVariant/1DefineAndMapBeans.kt",
    "content": "package kotlinVariant\n\nimport java.time.Instant\n\n//Data classes: Each entity definition in a single line! We get immutability, constructor, hashCode(), equals(), toString() for free.\nclass BlogEntity(val id: Long, val name: String, val posts: List<PostEntity>?)\nclass PostEntity(val id: Long, val date: Instant?, val author: AuthorEntity?, val text: String, comments: List<CommentEntity>?)\nclass AuthorEntity(val name: String, val email: String?)\nclass CommentEntity(val text: String, val author: AuthorEntity, date: Instant)\n//\"val\" makes the beans immutable (\"var\" fields can be modified).\n//The type \"String\" can never be null. The compiler enforces this!\n//Contrarily, the type \"String?\" is nullable. Null-safe types make the code much safer and avoid bloating null checks.\n\nclass BlogDTO(val id: Long, val name: String, val posts: List<PostDTO>?)\nclass PostDTO(val id: Long, val author: String, val date: String?, val text: String, commentsHref: String)\n\n//Usage of \"single expression function\": No body {} is necessary, if there is only a single expression.\n//Less boilerplate with the collection API : we can call map() directly on a list and it returns a list.\n//Implicit variable \"it\" (= parameter) makes lambda syntax even shorter. but you can also write \"para -> mapToBlogDTO(para)\".\nfun mapToBlogDTOs(entities: List<BlogEntity>) = entities.map { mapToBlogDTO(it) }\nfun mapToBlogDTO(entity: BlogEntity) = BlogDTO(\n        id = entity.id,\n        name = entity.name,\n        posts = entity.posts?.map { mapToPostDTO(it) }\n        //The Kotlin compiler forces me to consider that posts can be null.\n        //We can't call map() directly on posts, because it can be null.\n        //The null-safe call (\"?.\") invokes the operation only if posts are not null. Otherwise the whole expression is null.\n)\nfun mapToPostDTO(entity: PostEntity) = PostDTO(\n        //easy to read due to named arguments.\n        id = entity.id,\n        date = entity.date?.epochSecond.toString(), //\"?.\" (null safe call). if date is null, null is assigned. Otherwise the epochSecond is retrieved and assigned.\n        author = entity.author?.name ?: \"Anonymous\", //The elvis operator (\"?:\") makes Java's getNameOrDefault() a one-liner! If left side of \"?:\" is null, the right side is returned. Otherwise the left side is returned.\n        text = entity.text,\n        commentsHref = \"posts/${entity.id}/comments\" //String interpolation!\n)\n\n\n//warp up: kotlin code is...\n// a) extremely concise (data classes, single expression function, field accessor, compact list operations, null-safe calls, elvis operator),\n//lines of code: 201 lines (java) vs 18 lines (kotlin)! (and java doesn't include hashCode(), toString(), hashCode())\n//=> factor 11+ when it comes to code for structs and mapping!! no boilerplate in Kotlin.\n// b) more readable (named arguments) and\n// c) less error-prone (compiler enforced null safety, immutability, no manually written toString(), hashCode(), equals()).\n// extremely reduced boilerplate.\n"
  },
  {
    "path": "kotlin-examples/src/main/java/kotlinVariant/2ConditionsAndTypeSwitch.kt",
    "content": "package kotlinVariant\n\nimport java.sql.SQLException\nimport java.util.Locale\n\n//\"when\" is extremely powerful construct is Kotlin. It's much more than just a switch.\n\nfun getDefaultLocale(deliveryArea: String): Locale {\n    when (deliveryArea){\n        \"germany\", \"austria\", \"switzerland\" -> return Locale.GERMAN\n        \"usa\", \"great britain\", \"australia\" -> return Locale.ENGLISH\n        else -> throw IllegalArgumentException(\"Unsupported deliveryArea $deliveryArea\") //string interpolation\n    }\n}\n// or even shorter as a single expression function:\nfun getDefaultLocale2(deliveryArea: String) = when (deliveryArea){\n    \"germany\", \"austria\", \"switzerland\" -> Locale.GERMAN\n    \"usa\", \"great britain\", \"australia\" -> Locale.ENGLISH\n    else -> throw IllegalArgumentException(\"Unsupported deliveryArea $deliveryArea\")\n}\n\nfun getExceptionMessage(exception: Exception) = when (exception){\n    //concise type switches\n    is MyLabeledException -> exception.label //smart cast to MyLabeledException -> we can call label directly.\n    is SQLException -> \"${exception.message}. state: ${exception.sqlState}\" //string interpolation\n    else -> exception.message\n}\nclass MyLabeledException(val label: String) : RuntimeException(label)"
  },
  {
    "path": "kotlin-idiomatic/.gitignore",
    "content": ".idea/\ntarget/\n*.iml"
  },
  {
    "path": "kotlin-idiomatic/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>de.philipphauer.blog</groupId>\n    <artifactId>kotlin-idiomatic</artifactId>\n    <version>1.0-SNAPSHOT</version>\n\n    <properties>\n        <kotlin.version>1.1.1</kotlin.version>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.jetbrains.kotlin</groupId>\n            <artifactId>kotlin-stdlib-jre8</artifactId>\n            <version>${kotlin.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>com.jayway.jsonpath</groupId>\n            <artifactId>json-path</artifactId>\n            <version>2.2.0</version>\n        </dependency>\n        <dependency>\n            <groupId>com.vaadin</groupId>\n            <artifactId>vaadin-server</artifactId>\n            <version>8.0.3</version>\n        </dependency>\n        <dependency>\n            <groupId>com.vaadin</groupId>\n            <artifactId>vaadin-client-compiled</artifactId>\n            <version>8.0.3</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.httpcomponents</groupId>\n            <artifactId>httpclient</artifactId>\n            <version>4.5.3</version>\n        </dependency>\n        <dependency>\n            <groupId>org.jetbrains.kotlin</groupId>\n            <artifactId>kotlin-test</artifactId>\n            <version>${kotlin.version}</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.jetbrains.kotlin</groupId>\n            <artifactId>kotlin-stdlib-jre8</artifactId>\n            <version>${kotlin.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-dbcp2</artifactId>\n            <version>2.1.1</version>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <sourceDirectory>src/main/kotlin</sourceDirectory>\n        <plugins>\n            <plugin>\n                <artifactId>kotlin-maven-plugin</artifactId>\n                <groupId>org.jetbrains.kotlin</groupId>\n                <version>${kotlin.version}</version>\n                <executions>\n                    <execution>\n                        <id>compile</id>\n                        <phase>compile</phase>\n                        <goals>\n                            <goal>compile</goal>\n                        </goals>\n                    </execution>\n                    <execution>\n                        <id>test-compile</id>\n                        <phase>test-compile</phase>\n                        <goals>\n                            <goal>test-compile</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <version>3.5.1</version>\n                <configuration>\n                    <source>${java.version}</source>\n                    <target>${java.version}</target>\n                </configuration>\n                <executions>\n                    <!-- Replacing default-compile as it is treated specially by maven -->\n                    <execution>\n                        <id>default-compile</id>\n                        <phase>none</phase>\n                    </execution>\n                    <!-- Replacing default-testCompile as it is treated specially by maven -->\n                    <execution>\n                        <id>default-testCompile</id>\n                        <phase>none</phase>\n                    </execution>\n                    <execution>\n                        <id>java-compile</id>\n                        <phase>compile</phase>\n                        <goals>\n                            <goal>compile</goal>\n                        </goals>\n                    </execution>\n                    <execution>\n                        <id>java-test-compile</id>\n                        <phase>test-compile</phase>\n                        <goals>\n                            <goal>testCompile</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>"
  },
  {
    "path": "kotlin-idiomatic/src/main/kotlin/idiomaticKotlin/Apply.kt",
    "content": "package idiomaticKotlin\n\nimport org.apache.commons.dbcp2.BasicDataSource\n\n\nfun blub(){\n    // Don't\n    val dataSource = BasicDataSource()\n    dataSource.driverClassName = \"com.mysql.jdbc.Driver\"\n    dataSource.url = \"jdbc:mysql://domain:3309/db\"\n    dataSource.username = \"username\"\n    dataSource.password = \"password\"\n    dataSource.maxTotal = 40\n    dataSource.maxIdle = 40\n    dataSource.minIdle = 4\n}\n\n// Do\nval dataSource = BasicDataSource().apply {\n    driverClassName = \"com.mysql.jdbc.Driver\"\n    url = \"jdbc:mysql://domain:3309/db\"\n    username = \"username\"\n    password = \"password\"\n    maxTotal = 40\n    maxIdle = 40\n    minIdle = 4\n}"
  },
  {
    "path": "kotlin-idiomatic/src/main/kotlin/idiomaticKotlin/DefaultArgs.kt",
    "content": "package idiomaticKotlin\n\n// don't overload methods and constructors to realize default arguments (\"chaining\")\nfun find(name: String){\n    find(name, true)\n}\nfun find(name: String, recursive: Boolean){\n}\n\n// that are crutches. instead, Kotlin provides use named arguments\nfun find2(name: String, recursive: Boolean = true){\n}\n\n// in fact, default arguments removed nearly all use cases for method and constructor overloading."
  },
  {
    "path": "kotlin-idiomatic/src/main/kotlin/idiomaticKotlin/Destruction.kt",
    "content": "package idiomaticKotlin\n\n//destruction useful for\n\n//a) returning multiple values from a function. define an own data class or use Pair (but less expressive, no semantics)\ndata class ServiceConfig(val host: String, val port: Int)\nfun createServiceConfig(): ServiceConfig {\n    return ServiceConfig(\"api.domain.io\", 9389)\n}\n\nfun bla(){\n    val (host, port) = createServiceConfig()\n}\n\n//b) iterate over maps\nfun foo(){\n    val map = mapOf(\"api.domain.io\" to 9389, \"localhost\" to 8080)\n    for ((host, port) in map){\n        //...\n    }\n}\n\n"
  },
  {
    "path": "kotlin-idiomatic/src/main/kotlin/idiomaticKotlin/FunctionalProgramming.kt",
    "content": "package idiomaticKotlin\n\nimport com.jayway.jsonpath.JsonPath\nimport java.util.Locale\n\n// # Take advantages of functional programming support in Kotlin (better support then in java due to immutability and expression)\n// -> reduce side-effects (less error-prone, easier to understand, thread-safe)\n// (start with an enumeration of the relevant ##-points)\n\n// ## use immutability (val for variables and properties, immutable data classes, copy(), kotlin's collection api (read-only))\ndata class Person(var name: String)\n//better:\ndata class Person2(val name: String)\n\n//var x = \"hi\"\n//// better:\n//val y = \"hallo\"\n\n// ## use pure functions (without side-effects) where ever possible (therefore, use expressions and single expression functions)\n// ## use if, when, try-catch, single expression function! -> concise, expressive, stateless\n// expression instead of statements  (if, when) -> combine control structure with other expression concisely\n// Don't:\nfun getDefaultLocale(deliveryArea: String): Locale {\n    val deliverAreaLower = deliveryArea.toLowerCase()\n    if (deliverAreaLower == \"germany\" || deliverAreaLower == \"austria\") {\n        return Locale.GERMAN\n    }\n    if (deliverAreaLower == \"usa\" || deliverAreaLower == \"great britain\") {\n        return Locale.ENGLISH\n    }\n    if (deliverAreaLower == \"french\") {\n        return Locale.FRENCH\n    }\n    return Locale.ENGLISH\n}\n\n// Do:\nfun getDefaultLocale2(deliveryArea: String) = when (deliveryArea.toLowerCase()) {\n    \"germany\", \"austria\" -> Locale.GERMAN\n    \"usa\", \"great britain\" -> Locale.ENGLISH\n    \"french\" -> Locale.FRENCH\n    else -> Locale.ENGLISH\n}\n//println(getDefaultLocale(\"germany\"))\n// in general: consider if an `if` can be replace with a more concise `when` expression.\n\n//try-catch is also an expression!\nval json = \"\"\"{\"message\":\"HELLO\"}\"\"\"\nval message: String = try {\n    JsonPath.parse(json).read(\"message\")\n} catch (ex: Exception) {\n    json\n}\n//println(getMessage(\"\"\"{\"message\":\"HELLO\"}\"\"\")) //hello\n\n// ## use lambda expression to pass around blocks of code.\n\n\nfun main(args: Array<String>) {\n    println(getDefaultLocale(\"germany\"))\n    println(message) //hello\n}\n"
  },
  {
    "path": "kotlin-idiomatic/src/main/kotlin/idiomaticKotlin/InitBlock.kt",
    "content": "package idiomaticKotlin\n\nimport org.apache.http.client.HttpClient\nimport org.apache.http.impl.client.HttpClientBuilder\nimport java.util.concurrent.TimeUnit\n\n// think twice before you define a init block/constructor body\n// (first, named and default argument can help)\n// second, you can refer to primary constructor para in property initializers (and not only in the init block)\n// apply() can help to group initialization code and get along with a single expression.\n\n// Don't\nclass UsersClient(baseUrl: String, appName: String) {\n    private val usersUrl: String\n    private val httpClient: HttpClient\n    init {\n        usersUrl = \"$baseUrl/users\"\n        val builder = HttpClientBuilder.create()\n        builder.setUserAgent(appName)\n        builder.setConnectionTimeToLive(10, TimeUnit.SECONDS)\n        httpClient = builder.build()\n    }\n    fun getUsers(){\n        //call service using httpClient and usersUrl\n    }\n}\n\n// Do\nclass UsersClient2(baseUrl: String, appName: String) {\n    private val usersUrl = \"$baseUrl/users\"\n    private val httpClient = HttpClientBuilder.create().apply {\n        setUserAgent(appName)\n        setConnectionTimeToLive(10, TimeUnit.SECONDS)\n    }.build()\n    fun getUsers(){\n        //call service using httpClient and usersUrl\n    }\n}\n\n//- `with()` returns result of lambda (and it's invoked statically)\n//- `apply()` returns receiver obj (and it's invoked on receiver obj)"
  },
  {
    "path": "kotlin-idiomatic/src/main/kotlin/idiomaticKotlin/Mapping.kt",
    "content": "package idiomaticKotlin\n\nimport java.time.Instant\n\n\ndata class SnippetDTO(\n        val code: String,\n        val author: String,\n        val date: Instant\n)\ndata class SnippetEntity(\n        val code: String,\n        val author: AuthorEntity,\n        val date: Instant\n)\ndata class AuthorEntity(\n        val firstName: String,\n        val lastName: String\n)\n\n// Don't\nfun mapToDTO(entity: SnippetEntity): SnippetDTO {\n    val dto = SnippetDTO(\n            code = entity.code,\n            date = entity.date,\n            author = \"${entity.author.firstName} ${entity.author.lastName}\"\n    )\n    return dto\n}\n\n// Do\n// DONE easy, concise and readable mapping between value objects with single expression functions and named arguments\nfun mapToDTO2(entity: SnippetEntity) = SnippetDTO(\n        code = entity.code,\n        date = entity.date,\n        author = \"${entity.author.firstName} ${entity.author.lastName}\"\n)\n\n// even better: as an extension function\nfun SnippetEntity.toDTO() = SnippetDTO(\n        code = code,\n        date = date,\n        author = \"${author.firstName} ${author.lastName}\"\n)\n\n//val entity = SnippetEntity()\n//val dto = entity.toDTO()"
  },
  {
    "path": "kotlin-idiomatic/src/main/kotlin/idiomaticKotlin/NamedArgs.kt",
    "content": "package idiomaticKotlin\n\nclass SearchConfig {\n    fun setRoot(s: String): SearchConfig { return this }\n    fun setTerm(s: String): SearchConfig { return this }\n    fun setRecursive(s: Boolean): SearchConfig { return this }\n    fun setFollowSymlinks(s: Boolean): SearchConfig { return this }\n}\n\n//Back in Java, fluent setters where used to simulate named and default arguments and avoid huge parameter lists (error-prone and hard to read).\nval config = SearchConfig()\n        .setRoot(\"~/folder\")\n        .setTerm(\"kotlin\")\n        .setRecursive(true)\n        .setFollowSymlinks(true)\n\n//in kotlin, named and default arguments are built into the language\nval config2 = SearchConfig2(\n        root = \"~/folder\",\n        term = \"kotlin\",\n        recursive = true,\n        followSymlinks = true\n)\n\n//definition:\nclass SearchConfig2(\n        val root: String,\n        val term: String,\n        val recursive: Boolean = false,\n        val followSymlinks: Boolean = false\n)"
  },
  {
    "path": "kotlin-idiomatic/src/main/kotlin/idiomaticKotlin/Nullability.kt",
    "content": "package idiomaticKotlin\n\ndata class Order(val customer: Customer?)\ndata class Customer(val address: Address?)\ndata class Address(val city: String)\n\nfun ship(order: Order?){\n    //every time you do if-null-checks, hold on.\n    if (order == null || order.customer == null || order.customer.address == null){\n        throw IllegalArgumentException(\"Invalid Order\")\n    }\n    val city = order.customer.address.city\n}\n\nfun ship2(order: Order?){\n    // Often, you can use null-safe call (?.) or the elvis operator (?:) instead.\n    val city = order?.customer?.address?.city ?: throw IllegalArgumentException(\"Invalid Order\")\n}\n\n\ninterface Service\nclass CustomerService : Service {\n    fun getCustomer() {}\n}\n\nfun getMetrics(service: Service){\n    // also hold on for if-type-checks\n    if (service !is CustomerService) {\n        throw IllegalArgumentException(\"No CustomerService\")\n    }\n    service.getCustomer()\n}\n\nfun getMetrics2(service: Service){\n    //check type, (smart-)cast it and throw exception if the type is not the expected one. all in one expression!\n    service as? CustomerService ?: throw IllegalArgumentException(\"No CustomerService\")\n    service.getCustomer()\n}\n\n\nfun foo(order: Order?){\n    // avoid yelling !! where every possible. search for better solutions by verifying the variable up front and handle nulls. (quote book)\n    order!!.customer!!.address!!.city\n}\n\nfun findOrder(): Order? {\n    return null\n}\nfun dun(customer: Customer?){\n\n}\n\nfun handle(){\n    // Don't\n    val order: Order? = findOrder()\n    if (order != null){\n        dun(order.customer)\n    }\n\n    // with let(), there is no need for an extra variable\n    // can write as one expression\n    findOrder()?.let { dun(it.customer) }\n    findOrder()?.customer?.let(::dun)\n\n}"
  },
  {
    "path": "kotlin-idiomatic/src/main/kotlin/idiomaticKotlin/ObjectForStatelessFWImpls.kt",
    "content": "package idiomaticKotlin\n\nimport com.vaadin.data.Converter\nimport com.vaadin.data.Result\nimport com.vaadin.data.ValueContext\nimport java.time.Instant\nimport java.time.ZoneOffset\nimport java.time.format.DateTimeFormatter\nimport java.time.format.DateTimeParseException\nimport java.util.Locale\n\n// use object for singletons or implemention of a framework interface without state.\n// here: Vaadin 8's Converter interface\nobject StringToInstantConverter : Converter<String, Instant> {\n    private val DATE_FORMATTER = DateTimeFormatter.ofPattern(\"dd.MM.yyyy HH:mm:ss Z\")\n            .withLocale( Locale.UK )\n            .withZone( ZoneOffset.UTC )\n\n    override fun convertToModel(value: String?, context: ValueContext?) = try {\n        Result.ok(Instant.from(DATE_FORMATTER.parse(value)))\n    } catch (ex: DateTimeParseException) {\n        Result.error<Instant>(ex.message)\n    }\n\n    override fun convertToPresentation(value: Instant?, context: ValueContext?) =\n            DATE_FORMATTER.format(value)\n}\n\nfun main(args: Array<String>) {\n    val i = StringToInstantConverter.convertToPresentation(Instant.now(), null)\n    println(i)\n    val x = StringToInstantConverter.convertToModel(i, null)\n    println(x)\n}"
  },
  {
    "path": "kotlin-idiomatic/src/main/kotlin/idiomaticKotlin/Structs.kt",
    "content": "package idiomaticKotlin\n\n// due to `to` infix function to create a Pair and static methods to create lists and maps, defining structs is reasonable in kotlin.\n// but still not as good as in Python or JavaScript. But it's ok and way better then in Java.\nval customer = mapOf(\n        \"name\" to \"Clair Grube\",\n        \"age\" to 30,\n        \"languages\" to listOf(\"german\", \"english\"),\n        \"address\" to mapOf(\n                \"city\" to \"Leipzig\",\n                \"street\" to \"Karl-Liebknecht-Straße 1\",\n                \"zipCode\" to \"04107\"\n        )\n)"
  },
  {
    "path": "kotlin-idiomatic/src/main/kotlin/idiomaticKotlin/TopLevelExtensionFunctions.kt",
    "content": "package idiomaticKotlin\n\n// In Java, you often create static util methods in util classes.\nobject StringUtil {\n    fun countAmountOfX(string: String): Int{\n        return string.length - string.replace(\"x\", \"\").length\n    }\n}\n//StringUtil.countAmountOfX(\"xKotlinxFunx\") //3\n\n// In Kotlin, remove the unnecessary wrapping util class and use top-level functions instead\n// Often, you can additionally use extension functions, which increases readability (\"like a story\").\nfun String.countAmountOfX(): Int {\n    return length - replace(\"x\", \"\").length\n}\n//\"xKotlinxFunx\".countAmountOfX() //3\n\n"
  },
  {
    "path": "kotlin-idiomatic/src/main/kotlin/idiomaticKotlin/ValueObjects.kt",
    "content": "package idiomaticKotlin\n\n//without value object:\nfun send(target: String){}\n\n//expressive, readable, safe\nfun send(target: EmailAddress){}\n\n//with value object:\ndata class EmailAddress(val value: String)\n"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/.gitignore",
    "content": "target/\n.idea\n*.iml\ndev/\n\n"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/README.md",
    "content": "# Scaffolding for Kotlin, Spring Boot, Vaadin, Spring Data MongoDB\n\n## Development\n### Database\nStart/Stop MongoDB and MySQL\n```\n$ docker-compose up # -d flag for background\n$ docker-compose down\n```\nAccess MongoDB\n```\n$ mongo\n```\nAccess MySQL: \n1. a) Use a MySQL client and connect to localhost:3306 with root/root.\n2. b) use mysql cli: \n```\n$ mysql -h localhost -P 3306 --protocol=tcp -D dbname -u root -p dbname\nenter \"root\"\n```\n\n### Application\nStart MyApplication.kt out of your IDE. But don't forget to pass the yml (program arguments): \n```\n--spring.config.location=myapp.yaml\n```\n"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/TODO.md",
    "content": "- tests\n- screeny"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/docker-compose.yml",
    "content": "version: '2'\nservices:\n  mongo:\n    image: mongo:3.2.10\n    volumes:\n      - ./dev/mongodbdata:/data/db\n    ports:\n      - \"27017:27017\"\n    command: --smallfiles --profile=1 --slowms=0"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/myapp.yaml",
    "content": "server:\n  port: 8080\nspring:\n  data:\n    mongodb:\n      host: localhost\n      port: 27017\n      database: test\n  datasource:\n    url: \"jdbc:mysql://localhost:3306/dbname\"\n    username: \"root\"\n    password: \"root\"\n    driverClassName: \"com.mysql.cj.jdbc.Driver\"\nvaadin:\n  servlet:\n    productionMode: false\nmyapp:\n  requiredProp: \"value\"\n  authentication:\n    url: \"https://blabla.de\"\n    credentials:\n      userName: \"asdf\"\n      userPassword: \"asdf\""
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>de.philipphauer.blog</groupId>\n\t<artifactId>kotlin-spring-boot-vaadin-scaffolding</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.1.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.8</java.version>\n\t\t<kotlin.version>1.0.6</kotlin.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.jetbrains.kotlin</groupId>\n\t\t\t<artifactId>kotlin-stdlib</artifactId>\n\t\t\t<version>${kotlin.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.jetbrains.kotlin</groupId>\n\t\t\t<artifactId>kotlin-test-junit</artifactId>\n\t\t\t<version>${kotlin.version}</version>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-data-mongodb</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.vaadin</groupId>\n\t\t\t<artifactId>vaadin-spring-boot-starter</artifactId>\n\t\t\t<version>1.2.0</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.google.guava</groupId>\n\t\t\t<artifactId>guava</artifactId>\n\t\t\t<version>20.0</version>\n\t\t</dependency>\n\t</dependencies>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>com.vaadin</groupId>\n\t\t\t\t<artifactId>vaadin-bom</artifactId>\n\t\t\t\t<version>7.7.3</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\n\t<build>\n\t\t<sourceDirectory>src/main/kotlin</sourceDirectory>\n\t\t<testSourceDirectory>src/test/kotlin</testSourceDirectory>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<artifactId>kotlin-maven-plugin</artifactId>\n\t\t\t\t<groupId>org.jetbrains.kotlin</groupId>\n\t\t\t\t<version>${kotlin.version}</version>\n\t\t\t\t<configuration>\n\t\t\t\t\t<compilerPlugins>\n\t\t\t\t\t\t<plugin>spring</plugin>\n\t\t\t\t\t</compilerPlugins>\n\t\t\t\t</configuration>\n                <dependencies>\n                    <dependency>\n                        <groupId>org.jetbrains.kotlin</groupId>\n                        <artifactId>kotlin-maven-allopen</artifactId>\n                        <version>${kotlin.version}</version>\n                    </dependency>\n                </dependencies>\n\t\t\t\t<executions>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>compile</id>\n\t\t\t\t\t\t<phase>compile</phase>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>compile</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t</execution>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>test-compile</id>\n\t\t\t\t\t\t<phase>test-compile</phase>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>test-compile</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\t\t\t</plugin>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t<artifactId>maven-compiler-plugin</artifactId>\n\t\t\t\t<version>3.5.1</version>\n\t\t\t\t<executions>\n\t\t\t\t\t<!-- Replacing default-compile as it is treated specially by maven -->\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>default-compile</id>\n\t\t\t\t\t\t<phase>none</phase>\n\t\t\t\t\t</execution>\n\t\t\t\t\t<!-- Replacing default-testCompile as it is treated specially by maven -->\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>default-testCompile</id>\n\t\t\t\t\t\t<phase>none</phase>\n\t\t\t\t\t</execution>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>java-compile</id>\n\t\t\t\t\t\t<phase>compile</phase>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>compile</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t</execution>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>java-test-compile</id>\n\t\t\t\t\t\t<phase>test-compile</phase>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>testCompile</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t</execution>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>compile</id>\n\t\t\t\t\t\t<phase>compile</phase>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>compile</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t</execution>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>testCompile</id>\n\t\t\t\t\t\t<phase>test-compile</phase>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>testCompile</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\t\t\t</plugin>\n\t\t\t<plugin>\n\t\t\t\t<groupId>pl.project13.maven</groupId>\n\t\t\t\t<artifactId>git-commit-id-plugin</artifactId>\n\t\t\t\t<version>2.2.1</version>\n\t\t\t\t<executions>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<phase>validate</phase>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>revision</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\t\t\t\t<configuration>\n\t\t\t\t\t<abbrevLength>7</abbrevLength>\n\t\t\t\t\t<prefix>git</prefix>\n\t\t\t\t\t<dateFormat>yyyy-MM-dd'T'HH:mm:ss</dateFormat>\n\t\t\t\t\t<verbose>true</verbose>\n\t\t\t\t\t<dotGitDirectory>${project.basedir}/.git</dotGitDirectory>\n\t\t\t\t\t<skipPoms>true</skipPoms>\n\t\t\t\t\t<generateGitPropertiesFile>false</generateGitPropertiesFile>\n\t\t\t\t\t<failOnNoGitDirectory>false</failOnNoGitDirectory>\n\t\t\t\t\t<skip>false</skip>\n\t\t\t\t\t<gitDescribe>\n\t\t\t\t\t\t<skip>false</skip>\n\t\t\t\t\t\t<always>false</always>\n\t\t\t\t\t\t<abbrev>7</abbrev>\n\t\t\t\t\t\t<dirty>-dirty</dirty>\n\t\t\t\t\t\t<forceLongFormat>false</forceLongFormat>\n\t\t\t\t\t</gitDescribe>\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\t<repositories>\n\t\t<repository>\n\t\t\t<id>vaadin-addons</id>\n\t\t\t<url>http://maven.vaadin.com/vaadin-addons</url>\n\t\t</repository>\n\t</repositories>\n</project>\n"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/main/kotlin/de/philipphauer/blog/misc/PuttingTogether.kt",
    "content": "package de.philipphauer.blog.misc\n\nimport java.time.Instant\n\ndata class BlogEntity (\n        val author: AuthorEntity,\n        val date: Instant,\n        val content: String\n)\ndata class AuthorEntity(val firstName: String, val lastName: String)\n"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/main/kotlin/de/philipphauer/blog/misc/ValueObjects.kt",
    "content": "package de.philipphauer.blog.misc\n\nfun process1(emails: List<String>){}\n//vs\n\ndata class EmailAddress(val address: String)\n\nfun process2(emails: List<EmailAddress>){}\n//=> expressive, readable, safe\n"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/main/kotlin/de/philipphauer/blog/misc/constructorinjection/CRMClient.java",
    "content": "package de.philipphauer.blog.misc.constructorinjection;\n\npublic class CRMClient {\n}\n"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/main/kotlin/de/philipphauer/blog/misc/constructorinjection/ConstructorInjection.kt",
    "content": "package de.philipphauer.blog.misc.constructorinjection\n\nclass CustomerResourceKotlin(private val repo: CustomerRepository,\n                             private val client: CRMClient){\n    //that's it!\n}\n\n\n"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/main/kotlin/de/philipphauer/blog/misc/constructorinjection/CustomerRepository.java",
    "content": "package de.philipphauer.blog.misc.constructorinjection;\n\npublic class CustomerRepository {\n}\n"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/main/kotlin/de/philipphauer/blog/misc/constructorinjection/CustomerResource.java",
    "content": "package de.philipphauer.blog.misc.constructorinjection;\n\npublic class CustomerResource {\n\n    private CustomerRepository repo;\n    private CRMClient client;\n\n    public CustomerResource(CustomerRepository repo, CRMClient client) {\n        this.repo = repo;\n        this.client = client;\n    }\n}"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/main/kotlin/de/philipphauer/blog/misc/vaadin/ActionListenerLambdaExample.kt",
    "content": "package de.philipphauer.blog.misc.vaadin\n\nimport com.vaadin.ui.Button\nimport com.vaadin.ui.Notification\n\n\nclass ActionListenerLambdasExampleView {\n\n    private fun init() {\n        val button = Button(\"Click Me\")\n        button.addClickListener(::greet)\n\n        button.addClickListener{event -> greet(event)}\n\n        button.addClickListener{greet(it)}\n    }\n}\nprivate fun greet(event: Button.ClickEvent){\n    Notification.show(\"Hello!\")\n}\n\n"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/main/kotlin/de/philipphauer/blog/misc/vaadin/ActionListenerLambdaExampleJava.java",
    "content": "package de.philipphauer.blog.misc.vaadin;\n\nimport com.vaadin.ui.Button;\nimport com.vaadin.ui.Notification;\n\npublic class ActionListenerLambdaExampleJava {\n\n    private void bla(){\n        Button b = new Button();\n        b.addClickListener(this::greet);\n        b.addClickListener(event -> greet(event));\n        b.addClickListener(event -> {greet(event);});\n    }\n\n    private void greet(Button.ClickEvent event){\n        Notification.show(\"Hello!\");\n    }\n\n}\n"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/main/kotlin/de/philipphauer/blog/scaffolding/DummyDataCreator.kt",
    "content": "package de.philipphauer.blog.scaffolding\n\nimport de.philipphauer.blog.scaffolding.db.AuthorEntity\nimport de.philipphauer.blog.scaffolding.db.SnippetEntity\nimport de.philipphauer.blog.scaffolding.db.SnippetRepository\nimport de.philipphauer.blog.scaffolding.db.SnippetState\nimport org.slf4j.Logger\nimport org.springframework.boot.ApplicationArguments\nimport org.springframework.boot.ApplicationRunner\nimport org.springframework.stereotype.Component\nimport java.time.Instant\n\n@Component\nclass DummyDataCreator(val repo: SnippetRepository, val logger: Logger) : ApplicationRunner {\n\n    override fun run(args: ApplicationArguments) {\n        val count = repo.count()\n        if (count == 0L){\n            logger.info(\"Inserting dummy data...\")\n            val entity = SnippetEntity(\n                    id =    null, //set by db\n                    code = \"Select * From dual;\",\n                    date = Instant.now(),\n                    state = SnippetState.EXECUTION_SUCCESS,\n                    author = AuthorEntity(\n                            firstName = \"Peter\",\n                            lastName = \"Fischer\"\n                    )\n            )\n            repo.insert(entity)\n        }\n    }\n}\n"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/main/kotlin/de/philipphauer/blog/scaffolding/MyApplication.kt",
    "content": "package de.philipphauer.blog.scaffolding\n\nimport org.springframework.boot.SpringApplication\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration\nimport org.springframework.boot.autoconfigure.SpringBootApplication\nimport org.springframework.scheduling.annotation.EnableScheduling\n\n@SpringBootApplication\n@EnableAutoConfiguration\n@EnableScheduling\nclass MyApplication\n\nfun main(args: Array<String>) {\n    SpringApplication.run(MyApplication::class.java, *args)\n}\n"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/main/kotlin/de/philipphauer/blog/scaffolding/SpringConfiguration.kt",
    "content": "package de.philipphauer.blog.scaffolding\n\nimport org.slf4j.Logger\nimport org.slf4j.LoggerFactory\nimport org.springframework.beans.factory.InjectionPoint\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.context.annotation.Scope\n\n\n@Configuration\nclass SpringConfiguration {\n\n    @Bean\n    @Scope(\"prototype\")\n    fun logger(injectionPoint: InjectionPoint): Logger {\n        return LoggerFactory.getLogger(injectionPoint.methodParameter.containingClass)\n    }\n}"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/main/kotlin/de/philipphauer/blog/scaffolding/YamlConfigProps.kt",
    "content": "package de.philipphauer.blog.scaffolding\n\nimport org.springframework.boot.context.properties.ConfigurationProperties\nimport org.springframework.context.annotation.Configuration\nimport javax.validation.constraints.NotNull\n\n@Configuration\n@ConfigurationProperties(prefix = \"myapp\")\nclass MyAppProps {\n    @NotNull lateinit var requiredProp: String\n    var optionalProp: String? = null\n}\n\n@Configuration\n@ConfigurationProperties(prefix = \"myapp.authentication\")\nclass AuthenticationProps {\n    @NotNull lateinit var url: String\n    var credentials: Credentials? = null\n}\nclass Credentials{\n    @NotNull lateinit var userName: String\n    @NotNull lateinit var userPassword: String\n}\n\n"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/main/kotlin/de/philipphauer/blog/scaffolding/db/Entities.kt",
    "content": "package de.philipphauer.blog.scaffolding.db\n\nimport org.bson.types.ObjectId\nimport org.springframework.data.annotation.Id\nimport org.springframework.data.annotation.PersistenceConstructor\nimport org.springframework.data.mongodb.core.mapping.Document\nimport java.time.Instant\n\n@Document(collection=\"snippets\")\ndata class SnippetEntity @PersistenceConstructor constructor(\n        @Id val id: ObjectId? = null, //default parameter value requires @PersistenceConstructor\n        val code: String,\n        val author: AuthorEntity,\n        val date: Instant,\n        val state: SnippetState\n)\n\ndata class AuthorEntity(\n        val firstName: String,\n        val lastName: String\n)\n\nenum class SnippetState {EXECUTION_SUCCESS, EXECUTION_FAIL}"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/main/kotlin/de/philipphauer/blog/scaffolding/db/SnippetRepository.kt",
    "content": "package de.philipphauer.blog.scaffolding.db\n\nimport org.springframework.data.mongodb.repository.MongoRepository\n\ninterface SnippetRepository : MongoRepository<SnippetEntity, String>\n\n"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/main/kotlin/de/philipphauer/blog/scaffolding/rest/AdminResource.kt",
    "content": "package de.philipphauer.blog.scaffolding.rest\n\nimport com.google.common.io.Resources\nimport org.springframework.data.mongodb.core.MongoTemplate\nimport org.springframework.http.HttpStatus\nimport org.springframework.http.MediaType\nimport org.springframework.http.ResponseEntity\nimport org.springframework.scheduling.annotation.Scheduled\nimport org.springframework.stereotype.Controller\nimport org.springframework.web.bind.annotation.GetMapping\nimport org.springframework.web.bind.annotation.ResponseBody\nimport java.nio.charset.StandardCharsets\nimport java.time.Instant\nimport java.util.Properties\n\n\nprivate const val LAST_MONGO_CHECK_TOLERANCE_MS = 120000L\nprivate const val INITIAL_MONGO_CHECK_DELAY_MS = 2000L\nprivate const val MONGO_CHECK_INTERVAL_MS = 20000L\n\n@Controller\nclass AdminResource(val mongo: MongoTemplate) {\n\n    val versionDTO: VersionDTO by lazy { createVersionDTO() }\n\n    @Volatile private var lastSuccessfulStatusCheck: Instant = Instant.now()\n\n    @Scheduled(initialDelay = INITIAL_MONGO_CHECK_DELAY_MS, fixedDelay = MONGO_CHECK_INTERVAL_MS)\n    fun checkMongoStatus() {\n        val commandResult = mongo.executeCommand(\"{ping:1}\")\n        if (commandResult.ok()) {\n            lastSuccessfulStatusCheck = Instant.now()\n        }\n    }\n\n    @GetMapping(\"/status\", produces = arrayOf(MediaType.TEXT_PLAIN_VALUE))\n    fun getStatus(): ResponseEntity<String> =\n            if (lastSuccessfulStatusCheck.isAfter(Instant.now().minusMillis(LAST_MONGO_CHECK_TOLERANCE_MS)))\n                ResponseEntity<String>(\"OK\", HttpStatus.OK)\n            else\n                ResponseEntity<String>(\"MongoDB is not available since $lastSuccessfulStatusCheck\", HttpStatus.INTERNAL_SERVER_ERROR)\n\n    @GetMapping(\"/version\", produces = arrayOf(MediaType.APPLICATION_JSON_UTF8_VALUE))\n    @ResponseBody\n    fun getVersion() = versionDTO\n\n    @GetMapping(\"favicon.ico\")\n    fun favicon() = \"forward:/VAADIN/themes/mytheme/favicon.ico\"\n\n    @GetMapping(\"/\")\n    fun redirectToUI() = \"redirect:/ui\"\n\n    //the values won't be set when you start the application in the IDE.\n    private fun createVersionDTO() = readGitProperties().let {\n        VersionDTO(name = \"myapp\",\n                version = it.getProperty(\"api.version\"),\n                buildTime = it.getProperty(\"git.build.time\"),\n                commit = CommitVersionDTO(\n                        revision = it.getProperty(\"git.commit.id\"),\n                        message = it.getProperty(\"git.commit.message.full\"),\n                        author = it.getProperty(\"git.commit.user.name\"),\n                        time = it.getProperty(\"git.commit.time\"),\n                        branch = it.getProperty(\"git.branch\"))\n        )\n    }\n\n    private fun readGitProperties(): Properties {\n        val gitPropertyUrl = Resources.getResource(\"application.properties\")\n        val charSource = Resources.asCharSource(gitPropertyUrl, StandardCharsets.UTF_8)\n        return Properties().apply { load(charSource.openBufferedStream()) }\n    }\n}\n\ndata class VersionDTO(\n        val name: String,\n        val version: String,\n        val buildTime: String,\n        val commit: CommitVersionDTO\n)\n\ndata class CommitVersionDTO(\n        val revision: String,\n        val message: String,\n        val author: String,\n        val time: String,\n        val branch: String\n)\n"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/main/kotlin/de/philipphauer/blog/scaffolding/ui/Beans.kt",
    "content": "package de.philipphauer.blog.scaffolding.ui\n\nimport de.philipphauer.blog.scaffolding.db.SnippetState\nimport java.time.Instant\n\ndata class SnippetOverviewBean(\n        val code: String,\n        val author: String,\n        val date: Instant,\n        val state: SnippetState\n)\ndata class SnippetCreationBean(\n        var code: String? = null,\n        var author: String? = null\n)\n\nobject PropertyIds{\n    const val CODE = \"code\"\n    const val AUTHOR = \"author\"\n    const val DATE = \"date\"\n    const val STATE = \"state\"\n}\nobject Labels{\n    const val CODE = \"Code\"\n    const val AUTHOR = \"Author\"\n    const val DATE = \"Date\"\n    const val STATE = \"State\"\n}\n"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/main/kotlin/de/philipphauer/blog/scaffolding/ui/DetailsWindow.kt",
    "content": "package de.philipphauer.blog.scaffolding.ui\n\nimport com.vaadin.server.FontAwesome\nimport com.vaadin.server.Sizeable\nimport com.vaadin.shared.ui.label.ContentMode\nimport com.vaadin.ui.Button\nimport com.vaadin.ui.FormLayout\nimport com.vaadin.ui.Label\nimport com.vaadin.ui.Window\n\nclass DetailsWindow(snippet: SnippetOverviewBean) : Window(){\n    init {\n        caption = \"Snippet Details\"\n        isModal = true\n        val layout = FormLayout().apply {\n            setMargin(true)\n            isSpacing = true\n            val codeLabel = Label().apply {\n                contentMode = ContentMode.HTML\n                caption = Labels.CODE\n                value = snippet.code\n            }\n            val authorLabel = Label().apply {\n                caption = Labels.AUTHOR\n                value = snippet.author\n            }\n            val stateLabel = Label().apply {\n                contentMode = ContentMode.HTML\n                caption = Labels.STATE\n                value = \"${snippet.state.toIcon().html} ${snippet.state.toLabel()}\"\n            }\n            val closeButton = Button(\"Close\", FontAwesome.CLOSE).apply {\n                addClickListener { close() }\n            }\n            addComponents(codeLabel, authorLabel, stateLabel, closeButton)\n        }\n        setWidth(50F, Sizeable.Unit.PERCENTAGE)\n        setHeight(50F, Sizeable.Unit.PERCENTAGE)\n        center()\n        content = layout\n    }\n}\n"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/main/kotlin/de/philipphauer/blog/scaffolding/ui/EntityToBeanMapper.kt",
    "content": "package de.philipphauer.blog.scaffolding.ui\n\nimport de.philipphauer.blog.scaffolding.db.SnippetEntity\n\nfun mapToBeans(entities: List<SnippetEntity>) = entities.map(::mapToBean)\n\nfun mapToBean(entity: SnippetEntity) = SnippetOverviewBean(\n        code = entity.code,\n        date = entity.date,\n        author = \"${entity.author.firstName} ${entity.author.lastName}\",\n        state = entity.state\n)\n"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/main/kotlin/de/philipphauer/blog/scaffolding/ui/MainViewDisplay.kt",
    "content": "package de.philipphauer.blog.scaffolding.ui\n\nimport com.vaadin.navigator.View\nimport com.vaadin.navigator.ViewDisplay\nimport com.vaadin.spring.annotation.SpringViewDisplay\nimport com.vaadin.ui.Component\nimport com.vaadin.ui.Panel\nimport com.vaadin.ui.themes.ValoTheme\n\n\n@SpringViewDisplay\nclass MainViewDisplay : Panel(), ViewDisplay {\n    init {\n        styleName = ValoTheme.PANEL_BORDERLESS\n    }\n\n    override fun showView(view: View) {\n        content = view as Component\n    }\n}"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/main/kotlin/de/philipphauer/blog/scaffolding/ui/MyAppUI.kt",
    "content": "package de.philipphauer.blog.scaffolding.ui\n\nimport com.vaadin.annotations.Theme\nimport com.vaadin.server.FontAwesome\nimport com.vaadin.server.Sizeable\nimport com.vaadin.server.VaadinRequest\nimport com.vaadin.shared.ui.label.ContentMode\nimport com.vaadin.spring.annotation.SpringUI\nimport com.vaadin.spring.navigator.SpringNavigator\nimport com.vaadin.ui.Label\nimport com.vaadin.ui.UI\nimport com.vaadin.ui.VerticalLayout\nimport com.vaadin.ui.themes.ValoTheme\n\n\n@SpringUI(path = \"ui\")\n@Theme(\"mytheme\")\nclass MyAppUI(val mainContent: MainViewDisplay, navigator: SpringNavigator) : UI() {\n\n    val navigationPresenter = NavigationPresenter(navigator)\n\n    override fun init(request: VaadinRequest) {\n        page.setTitle(\"Kotlin, Spring Boot, Vaadin\")\n        content = VerticalLayout(createHeader(), navigationPresenter.menu, mainContent).apply{\n            setExpandRatio(mainContent, 1f)\n            setHeight(100f, Sizeable.Unit.PERCENTAGE)\n            setMargin(true)\n        }\n        navigationPresenter.disableCurrentMenuItem()\n    }\n\n    private fun  createHeader(): Label {\n        val heading = Label(\"${FontAwesome.CODE.html} My Application with Kotlin, Spring Boot and Vaadin\").apply {\n            styleName = ValoTheme.LABEL_HUGE\n            contentMode = ContentMode.HTML\n        }\n        return heading\n    }\n}"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/main/kotlin/de/philipphauer/blog/scaffolding/ui/NavigationPresenter.kt",
    "content": "package de.philipphauer.blog.scaffolding.ui\n\nimport com.vaadin.navigator.View\nimport com.vaadin.server.FontAwesome\nimport com.vaadin.server.Page\nimport com.vaadin.server.Resource\nimport com.vaadin.spring.annotation.SpringView\nimport com.vaadin.spring.navigator.SpringNavigator\nimport com.vaadin.ui.MenuBar\nimport com.vaadin.ui.themes.ValoTheme\nimport de.philipphauer.blog.scaffolding.ui.views.CreateSnippetView\nimport de.philipphauer.blog.scaffolding.ui.views.ErrorView\nimport de.philipphauer.blog.scaffolding.ui.views.OverviewView\n\nclass NavigationPresenter(val navigator: SpringNavigator){\n\n    val menu: MenuBar\n    private val viewNameToMenuBar: Map<String, MenuBar.MenuItem>\n\n    init {\n        navigator.setErrorView(ErrorView::class.java)\n        menu = MenuBar()\n        menu.addStyleName(ValoTheme.MENUBAR_SMALL)\n        menu.addStyleName(ValoTheme.MENUBAR_BORDERLESS)\n\n        val overviewItem = createMenuItem(OverviewView.LABEL, OverviewView::class.java, FontAwesome.LIST)\n        val createItem = createMenuItem(CreateSnippetView.LABEL, CreateSnippetView::class.java, FontAwesome.CODE)\n        viewNameToMenuBar = mapOf(OverviewView.VIEW_NAME to overviewItem,\n                CreateSnippetView.VIEW_NAME to createItem)\n    }\n\n    private fun createMenuItem(label: String, viewClass: Class<out View>, icon: Resource): MenuBar.MenuItem {\n        val viewAnnotation = viewClass.getDeclaredAnnotation(SpringView::class.java)\n        return menu.addItem(label, icon, MenuBar.Command { navigateTo(viewAnnotation.name )})\n    }\n\n    fun navigateTo(view: String) {\n        navigator.navigateTo(view)\n        disableMenuItem(view)\n    }\n\n    fun disableMenuItem(view: String) {\n        for ((viewName, menuItem) in viewNameToMenuBar){\n            menuItem.isEnabled = viewName != view\n        }\n    }\n\n    fun disableCurrentMenuItem() {\n        when (Page.getCurrent().uriFragment?.removePrefix(\"!\")) {\n            CreateSnippetView.VIEW_NAME -> disableMenuItem(CreateSnippetView.VIEW_NAME)\n            null, OverviewView.VIEW_NAME -> disableMenuItem(OverviewView.VIEW_NAME)\n        }\n    }\n}"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/main/kotlin/de/philipphauer/blog/scaffolding/ui/UiMisc.kt",
    "content": "package de.philipphauer.blog.scaffolding.ui\n\nimport com.vaadin.server.FontAwesome\nimport de.philipphauer.blog.scaffolding.db.SnippetState\n\nfun SnippetState.toIcon() = when (this){\n    SnippetState.EXECUTION_SUCCESS -> FontAwesome.THUMBS_O_UP\n    SnippetState.EXECUTION_FAIL -> FontAwesome.THUMBS_O_DOWN\n}\n\nfun SnippetState.toLabel() = when (this){\n    SnippetState.EXECUTION_SUCCESS -> \"Successfully executed\"\n    SnippetState.EXECUTION_FAIL -> \"Execution failed\"\n}"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/main/kotlin/de/philipphauer/blog/scaffolding/ui/views/CreateSnippetView.kt",
    "content": "package de.philipphauer.blog.scaffolding.ui.views\n\nimport com.vaadin.data.fieldgroup.BeanFieldGroup\nimport com.vaadin.data.fieldgroup.PropertyId\nimport com.vaadin.navigator.View\nimport com.vaadin.navigator.ViewChangeListener\nimport com.vaadin.server.FontAwesome\nimport com.vaadin.server.Sizeable\nimport com.vaadin.spring.annotation.SpringView\nimport com.vaadin.ui.Button\nimport com.vaadin.ui.FormLayout\nimport com.vaadin.ui.Notification\nimport com.vaadin.ui.TextArea\nimport com.vaadin.ui.TextField\nimport com.vaadin.ui.VerticalLayout\nimport de.philipphauer.blog.scaffolding.db.SnippetRepository\nimport de.philipphauer.blog.scaffolding.ui.Labels\nimport de.philipphauer.blog.scaffolding.ui.PropertyIds\nimport de.philipphauer.blog.scaffolding.ui.SnippetCreationBean\nimport javax.annotation.PostConstruct\n\n\n@SpringView(name = CreateSnippetView.VIEW_NAME)\nclass CreateSnippetView(val repo: SnippetRepository) : VerticalLayout(), View {\n\n    companion object{\n        const val VIEW_NAME = \"create\"\n        const val LABEL = \"Create Snippet\"\n    }\n\n    lateinit var fieldGroup: BeanFieldGroup<SnippetCreationBean>\n\n    override fun enter(event: ViewChangeListener.ViewChangeEvent) {\n    }\n\n    @PostConstruct\n    internal fun init() {\n        val form = CreateSnippetForm().apply {\n            createButton.addClickListener { createSnippet() }\n        }\n        val emptySnippet = SnippetCreationBean()\n        fieldGroup = BeanFieldGroup.bindFieldsUnbuffered(emptySnippet, form)\n        setSizeFull()\n        addComponent(form)\n        setExpandRatio(form, 1f)\n    }\n\n    private fun createSnippet() {\n        if (fieldGroup.isValid){\n            Notification.show(\"Snippet: ${fieldGroup.itemDataSource.bean}\")\n            // Snippet creation should go here...\n        } else {\n            Notification.show(\"Invalid!\", Notification.Type.ERROR_MESSAGE)\n        }\n    }\n}\n\nclass CreateSnippetForm : FormLayout() {\n\n    @PropertyId(PropertyIds.CODE)\n    val code = TextArea(Labels.CODE).apply {\n        nullRepresentation = \"\"\n        setWidth(100f, Sizeable.Unit.PERCENTAGE)\n        isRequired = true\n        addStyleName(\"monospace\")\n    }\n\n    @PropertyId(PropertyIds.AUTHOR)\n    val author = TextField(Labels.AUTHOR).apply {\n        nullRepresentation = \"\"\n        setWidth(100f, Sizeable.Unit.PERCENTAGE)\n    }\n\n    val createButton = Button(\"Create Snippet\", FontAwesome.CODE)\n\n    init {\n        setSizeFull()\n        isSpacing = true\n        addComponents(code, author, createButton)\n    }\n}"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/main/kotlin/de/philipphauer/blog/scaffolding/ui/views/ErrorView.kt",
    "content": "package de.philipphauer.blog.scaffolding.ui.views\n\nimport com.vaadin.navigator.View\nimport com.vaadin.navigator.ViewChangeListener.ViewChangeEvent\nimport com.vaadin.spring.annotation.SpringComponent\nimport com.vaadin.spring.annotation.SpringView\nimport com.vaadin.spring.annotation.UIScope\nimport com.vaadin.ui.Label\nimport com.vaadin.ui.VerticalLayout\nimport javax.annotation.PostConstruct\n\n\n@SpringComponent\n@UIScope\n@SpringView\nclass ErrorView : VerticalLayout(), View {\n\n    override fun enter(event: ViewChangeEvent) {\n    }\n\n    @PostConstruct\n    internal fun init() {\n        addComponent(Label(\"Invalid View Fragment in URL!\"))\n    }\n}"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/main/kotlin/de/philipphauer/blog/scaffolding/ui/views/OverviewView.kt",
    "content": "package de.philipphauer.blog.scaffolding.ui.views\n\nimport com.vaadin.data.util.BeanItem\nimport com.vaadin.data.util.BeanItemContainer\nimport com.vaadin.data.util.converter.Converter\nimport com.vaadin.navigator.View\nimport com.vaadin.navigator.ViewChangeListener\nimport com.vaadin.spring.annotation.SpringView\nimport com.vaadin.ui.Button\nimport com.vaadin.ui.Table\nimport com.vaadin.ui.UI\nimport com.vaadin.ui.VerticalLayout\nimport com.vaadin.ui.themes.ValoTheme\nimport de.philipphauer.blog.scaffolding.MyAppProps\nimport de.philipphauer.blog.scaffolding.db.SnippetRepository\nimport de.philipphauer.blog.scaffolding.ui.DetailsWindow\nimport de.philipphauer.blog.scaffolding.ui.Labels\nimport de.philipphauer.blog.scaffolding.ui.PropertyIds\nimport de.philipphauer.blog.scaffolding.ui.SnippetOverviewBean\nimport de.philipphauer.blog.scaffolding.ui.mapToBeans\nimport java.time.Instant\nimport java.time.ZoneOffset\nimport java.time.format.DateTimeFormatter\nimport java.util.Locale\nimport javax.annotation.PostConstruct\n\n\n@SpringView(name = OverviewView.VIEW_NAME)\nclass OverviewView(val repo: SnippetRepository, val props: MyAppProps) : VerticalLayout(), View {\n\n    companion object {\n        const val VIEW_NAME = \"\"\n        const val LABEL = \"Overview\"\n    }\n\n    override fun enter(event: ViewChangeListener.ViewChangeEvent) {\n    }\n\n    @PostConstruct\n    internal fun init() {\n        val snippetEntities = repo.findAll()\n        val snippetBeans = mapToBeans(snippetEntities)\n        val container = BeanItemContainer(SnippetOverviewBean::class.java, snippetBeans)\n        val table = Table(null, container).apply {\n            setSizeFull()\n            setColumnHeader(PropertyIds.CODE, Labels.CODE)\n            setColumnHeader(PropertyIds.AUTHOR, Labels.AUTHOR)\n            setColumnHeader(PropertyIds.DATE, Labels.DATE)\n            setColumnHeader(PropertyIds.STATE, Labels.STATE)\n            sort(arrayOf(PropertyIds.DATE), booleanArrayOf(false))\n            addGeneratedColumn(PropertyIds.CODE, ShortenedValueColumnGenerator)\n            addGeneratedColumn(\"Details\", ::generateDetailsButton)\n            setConverter(PropertyIds.DATE, StringToInstantConverter)\n        }\n        setSizeFull()\n        addComponent(table)\n    }\n}\n\n//a) ColumnGenerator as singleton object (you want to want to group multiple fields and methods OR when your have more than one method (e.g. Converter))\nprivate object ShortenedValueColumnGenerator : Table.ColumnGenerator {\n    private val MAX_LENGTH = 20\n\n    override fun generateCell(source: Table, itemId: Any, columnId: Any): Any?{\n        val log = source.getItem(itemId).getItemProperty(columnId).value as? String\n        return log?.shortenWithEllipsis()\n    }\n\n    fun String.shortenWithEllipsis(): String{\n        if (this.length > MAX_LENGTH){\n            return \"${this.substring(0, MAX_LENGTH)}...\"\n        }\n        return this\n    }\n}\n\n//b) ColumnGenerator as top-level function. Pass as method reference. very concise.\nprivate fun generateDetailsButton(source: Table, itemId: Any, columnId: Any) = Button(\"Details\").apply {\n    addStyleName(ValoTheme.BUTTON_LINK)\n    addClickListener {\n        val item = source.getItem(itemId) as BeanItem<SnippetOverviewBean>\n        val window = DetailsWindow(item.bean)\n        UI.getCurrent().addWindow(window)\n    }\n}\n\n//\"object\" singleton useful for interfaces with more than one method. only if stateless.\nobject StringToInstantConverter : Converter<String, Instant> {\n    private val DATE_FORMATTER: DateTimeFormatter = DateTimeFormatter.ofPattern(\"dd.MM.yyyy HH:mm:ss Z\")\n            .withLocale(Locale.UK)\n            .withZone(ZoneOffset.UTC)\n\n    override fun convertToPresentation(value: Instant?, targetType: Class<out String>?, locale: Locale?)\n            = DATE_FORMATTER.format(value)!!\n\n    override fun convertToModel(value: String?, targetType: Class<out Instant>?, locale: Locale?): Instant {\n        throw UnsupportedOperationException(\"Not yet implemented\")\n    }\n\n    //enjoy the beauty of the following method definitions:\n    override fun getPresentationType() = String::class.java\n    override fun getModelType() = Instant::class.java\n}"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/main/resources/VAADIN/themes/mytheme/mytheme.scss",
    "content": "@import \"../valo/valo.scss\";\n\n@mixin mytheme {\n  @include valo;\n\n  .monospace {\n    font-family: monospace;\n  }\n}"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/main/resources/VAADIN/themes/mytheme/styles.scss",
    "content": "@import \"mytheme\";\n\n.mytheme {\n  @include mytheme;\n}"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/main/resources/application.properties",
    "content": "# see http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html\n\n# productionMode is configured in yaml\nvaadin.servlet.heartbeatInterval=60\nvaadin.servlet.closeIdleSessions=true\n# see https://github.com/vaadin/spring/blob/master/vaadin-spring-boot/src/main/java/com/vaadin/spring/boot/internal/VaadinServletConfigurationProperties.java\n\n# deactivate default favicon delivery. use my dedicated resource for this. this resources uses the favicon in the vaadin theme.\nspring.mvc.favicon.enabled=false\n\n# git-commit-id-plugin, spring-boot requires special @ delimiter\ngit.tags=@git.tags@\ngit.branch=@git.branch@\ngit.dirty=@git.dirty@\ngit.remote.origin.url=@git.remote.origin.url@\ngit.commit.id=@git.commit.id@\ngit.commit.id.abbrev=@git.commit.id.abbrev@\ngit.commit.id.describe=@git.commit.id.describe@\ngit.commit.id.describe-short=@git.commit.id.describe-short@\ngit.commit.user.name=@git.commit.user.name@\ngit.commit.user.email=@git.commit.user.email@\ngit.commit.message.full=@git.commit.message.full@\ngit.commit.message.short=@git.commit.message.short@\ngit.commit.time=@git.commit.time@\ngit.closest.tag.name=@git.closest.tag.name@\ngit.closest.tag.commit.count=@git.closest.tag.commit.count@\n\ngit.build.user.name=@git.build.user.name@\ngit.build.user.email=@git.build.user.email@\ngit.build.time=@git.build.time@\ngit.build.host=@git.build.host@\ngit.build.version=@git.build.version@\n\n# maven built-in\napi.version=@project.version@\napi.buildtime=@buildtime@"
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/main/resources/banner.txt",
    "content": "  _  __     _   _ _                        _        \n | |/ /    | | | (_)                      | |       \n | ' / ___ | |_| |_ _ __    _ __ ___   ___| | _____ \n |  < / _ \\| __| | | '_ \\  | '__/ _ \\ / __| |/ / __|\n | . \\ (_) | |_| | | | | | | | | (_) | (__|   <\\__ \\\n |_|\\_\\___/ \\__|_|_|_| |_| |_|  \\___/ \\___|_|\\_\\___/\n                                                    \n                                                    "
  },
  {
    "path": "kotlin-spring-boot-vaadin-scaffolding/src/test/kotlin/de/philipphauer/blog/scaffolding/DummyDataCreatorTest.kt",
    "content": "package de.philipphauer.blog.scaffolding\n\nclass DummyDataCreatorTest"
  },
  {
    "path": "modern-best-practices-testing-java/.gitignore",
    "content": "HELP.md\n/target/\n!.mvn/wrapper/maven-wrapper.jar\n\n### STS ###\n.apt_generated\n.classpath\n.factorypath\n.project\n.settings\n.springBeans\n.sts4-cache\n\n### IntelliJ IDEA ###\n.idea\n*.iws\n*.iml\n*.ipr\n\n### NetBeans ###\n/nbproject/private/\n/nbbuild/\n/dist/\n/nbdist/\n/.nb-gradle/\n/build/\n"
  },
  {
    "path": "modern-best-practices-testing-java/docker-compose.yml",
    "content": "version: '3.1'\nservices:\n  db:\n    image: \"postgres:11.2-alpine\"\n    environment:\n      POSTGRES_PASSWORD: password\n      # user: postgres\n      # pw: password\n    ports:\n      - \"5432:5432\"\n  adminer:\n    image: adminer\n    restart: always\n    ports:\n      - \"90:8080\"\n"
  },
  {
    "path": "modern-best-practices-testing-java/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>com.phauer</groupId>\n    <artifactId>modern-best-practices-testing-java</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>11</java.version>\n        <vaadin.version>13.0.0</vaadin.version>\n        <testcontainers.version>1.10.7</testcontainers.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-jdbc</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-mongodb</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.vaadin</groupId>\n            <artifactId>vaadin-spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.postgresql</groupId>\n            <artifactId>postgresql</artifactId>\n            <version>42.2.5</version>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n            <exclusions>\n                <exclusion>\n                    <groupId>junit</groupId>\n                    <artifactId>junit</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n        <dependency>\n            <groupId>org.assertj</groupId>\n            <artifactId>assertj-core</artifactId>\n            <version>3.11.1</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.junit.jupiter</groupId>\n            <artifactId>junit-jupiter</artifactId>\n            <version>5.4.0</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.testcontainers</groupId>\n            <artifactId>testcontainers</artifactId>\n            <version>${testcontainers.version}</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.testcontainers</groupId>\n            <artifactId>postgresql</artifactId>\n            <version>${testcontainers.version}</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.squareup.okhttp3</groupId>\n            <artifactId>mockwebserver</artifactId>\n            <version>3.13.1</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.github.mvysny.kaributesting</groupId>\n            <artifactId>karibu-testing-v10</artifactId>\n            <version>1.1.4</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.awaitility</groupId>\n            <artifactId>awaitility</artifactId>\n            <version>4.0.1</version>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>com.vaadin</groupId>\n                <artifactId>vaadin-bom</artifactId>\n                <version>${vaadin.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "modern-best-practices-testing-java/src/main/java/com/phauer/modernunittesting/FuturePlayground.java",
    "content": "package com.phauer.modernunittesting;\n\nimport org.springframework.scheduling.annotation.Scheduled;\n\nimport java.util.Locale;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.ExecutionException;\n\npublic class FuturePlayground {\n\n    public void bla() throws ExecutionException, InterruptedException {\n        CompletableFuture<String> usFuture = CompletableFuture.supplyAsync(() -> doBusinessLogic(Locale.US));\n        CompletableFuture<String> germanyFuture = CompletableFuture.supplyAsync(() -> doBusinessLogic(Locale.GERMANY));\n        String usResult = usFuture.get();\n        String germanyResult = germanyFuture.get();\n    }\n\n    @Scheduled\n    public void start() {\n\n    }\n\n    String doBusinessLogic(Locale locale) {\n        return \"asdf\";\n    }\n}\n\n\n"
  },
  {
    "path": "modern-best-practices-testing-java/src/main/java/com/phauer/modernunittesting/MainLayout.java",
    "content": "package com.phauer.modernunittesting;\n\nimport com.vaadin.flow.component.html.Div;\nimport com.vaadin.flow.component.page.Push;\nimport com.vaadin.flow.component.page.Viewport;\nimport com.vaadin.flow.router.RouterLayout;\n\n@Push\n@Viewport(\"width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes\")\npublic class MainLayout extends Div implements RouterLayout {\n}\n"
  },
  {
    "path": "modern-best-practices-testing-java/src/main/java/com/phauer/modernunittesting/ModernUnitTestingApplication.java",
    "content": "package com.phauer.modernunittesting;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ModernUnitTestingApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ModernUnitTestingApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "modern-best-practices-testing-java/src/main/java/com/phauer/modernunittesting/PriceCalculator.java",
    "content": "package com.phauer.modernunittesting;\n\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class PriceCalculator {\n}\n"
  },
  {
    "path": "modern-best-practices-testing-java/src/main/java/com/phauer/modernunittesting/ProductController.java",
    "content": "package com.phauer.modernunittesting;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n@RestController\npublic class ProductController {\n\n    private final ProductDAO dao;\n\n    public ProductController(ProductDAO dao, TaxServiceClient client, PriceCalculator calculator) {\n        this.dao = dao;\n    }\n\n    @GetMapping(\"/products\")\n    public List<ProductDTO> getProducts() {\n        List<ProductEntity> products = dao.findProducts();\n        return toDto(products);\n    }\n\n    private List<ProductDTO> toDto(List<ProductEntity> products) {\n        return products.stream()\n                .map(this::toDto)\n                .collect(Collectors.toList());\n    }\n\n    private ProductDTO toDto(ProductEntity entity) {\n        return new ProductDTO()\n                .setId(entity.getId())\n                .setName(entity.getName())\n                .setPrice(0.5);\n    }\n}\n"
  },
  {
    "path": "modern-best-practices-testing-java/src/main/java/com/phauer/modernunittesting/ProductDAO.java",
    "content": "package com.phauer.modernunittesting;\n\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.stereotype.Component;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.List;\n\n@Component\npublic class ProductDAO {\n\n    private final JdbcTemplate template;\n\n    public ProductDAO(JdbcTemplate template) {\n        this.template = template;\n    }\n\n    public List<ProductEntity> findProducts() {\n        return template.query(\"select * from products;\", this::map);\n    }\n\n    private ProductEntity map(ResultSet resultSet, int i) throws SQLException {\n        return new ProductEntity()\n                .setId(resultSet.getString(\"id\"))\n                .setName(resultSet.getString(\"name\"));\n    }\n\n}\n\n\n"
  },
  {
    "path": "modern-best-practices-testing-java/src/main/java/com/phauer/modernunittesting/ProductDTO.java",
    "content": "package com.phauer.modernunittesting;\n\nimport java.util.Objects;\nimport java.util.StringJoiner;\n\npublic class ProductDTO {\n    private String id;\n    private String name;\n    private double price;\n\n    public double getPrice() {\n        return price;\n    }\n\n    public ProductDTO setPrice(double price) {\n        this.price = price;\n        return this;\n    }\n\n    public String getId() {\n        return id;\n    }\n\n    public ProductDTO setId(String id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public ProductDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return new StringJoiner(\", \", ProductDTO.class.getSimpleName() + \"[\", \"]\")\n                .add(\"id='\" + id + \"'\")\n                .add(\"name='\" + name + \"'\")\n                .add(\"price=\" + price)\n                .toString();\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        ProductDTO that = (ProductDTO) o;\n        return Double.compare(that.price, price) == 0 &&\n                Objects.equals(id, that.id) &&\n                Objects.equals(name, that.name);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(id, name, price);\n    }\n}\n"
  },
  {
    "path": "modern-best-practices-testing-java/src/main/java/com/phauer/modernunittesting/ProductEntity.java",
    "content": "package com.phauer.modernunittesting;\n\npublic class ProductEntity {\n    private String id;\n    private String name;\n    private String category;\n    private String description;\n    private int stockAmount;\n\n    public String getDescription() {\n        return description;\n    }\n\n    public ProductEntity setDescription(String description) {\n        this.description = description;\n        return this;\n    }\n\n    public int getStockAmount() {\n        return stockAmount;\n    }\n\n    public ProductEntity setStockAmount(int stockAmount) {\n        this.stockAmount = stockAmount;\n        return this;\n    }\n\n    public String getCategory() {\n        return category;\n    }\n\n    public ProductEntity setCategory(String category) {\n        this.category = category;\n        return this;\n    }\n\n    public String getId() {\n        return id;\n    }\n\n    public ProductEntity setId(String id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public ProductEntity setName(String name) {\n        this.name = name;\n        return this;\n    }\n}\n"
  },
  {
    "path": "modern-best-practices-testing-java/src/main/java/com/phauer/modernunittesting/ProductModel.java",
    "content": "package com.phauer.modernunittesting;\n\nimport java.util.Objects;\nimport java.util.StringJoiner;\n\npublic class ProductModel {\n    private String id;\n    private String name;\n\n    public String getId() {\n        return id;\n    }\n\n    public ProductModel setId(String id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public ProductModel setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        ProductModel that = (ProductModel) o;\n        return Objects.equals(id, that.id) &&\n                Objects.equals(name, that.name);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(id, name);\n    }\n\n    @Override\n    public String toString() {\n        return new StringJoiner(\", \", ProductModel.class.getSimpleName() + \"[\", \"]\")\n                .add(\"id='\" + id + \"'\")\n                .add(\"name='\" + name + \"'\")\n                .toString();\n    }\n}\n"
  },
  {
    "path": "modern-best-practices-testing-java/src/main/java/com/phauer/modernunittesting/ProductView.java",
    "content": "package com.phauer.modernunittesting;\n\nimport com.vaadin.flow.component.ClickEvent;\nimport com.vaadin.flow.component.button.Button;\nimport com.vaadin.flow.component.grid.Grid;\nimport com.vaadin.flow.component.orderedlayout.VerticalLayout;\nimport com.vaadin.flow.router.PageTitle;\nimport com.vaadin.flow.router.Route;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n@Route(value = \"\", layout = MainLayout.class)\n@PageTitle(\"Hello Vaadin\")\npublic class ProductView extends VerticalLayout {\n\n    private ProductDAO dao;\n\n    private Grid<ProductModel> grid;\n\n    public ProductView(ProductDAO dao) {\n        this.dao = dao;\n        initView();\n    }\n\n    private void initView() {\n        Button button = new Button(\"Load Products\");\n        button.addClickListener(this::loadButtonHandler);\n\n        this.grid = new Grid<>(ProductModel.class);\n\n        add(button, grid);\n    }\n\n    private void loadButtonHandler(ClickEvent event) {\n        var products = dao.findProducts();\n        grid.setItems(toModel(products));\n    }\n\n    private List<ProductModel> toModel(List<ProductEntity> products) {\n        return products.stream()\n                .map(this::toModel)\n                .collect(Collectors.toList());\n    }\n\n    private ProductModel toModel(ProductEntity entity) {\n        return new ProductModel()\n                .setId(entity.getId())\n                .setName(entity.getName());\n    }\n}\n"
  },
  {
    "path": "modern-best-practices-testing-java/src/main/java/com/phauer/modernunittesting/SchemaCreator.java",
    "content": "package com.phauer.modernunittesting;\n\nimport org.springframework.boot.ApplicationArguments;\nimport org.springframework.boot.ApplicationRunner;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class SchemaCreator implements ApplicationRunner {\n\n    private final JdbcTemplate jdbcTemplate;\n\n    public SchemaCreator(JdbcTemplate jdbcTemplate) {\n        this.jdbcTemplate = jdbcTemplate;\n    }\n\n    @Override\n    public void run(ApplicationArguments args) throws Exception {\n        createSchema(jdbcTemplate);\n\n        jdbcTemplate.execute(\"insert into products(id, name) values \" +\n                \"('1', 'notebook'), \" +\n                \"('2', 'smartphone'), \" +\n                \"('3', 'cup') \" +\n                \"ON CONFLICT(id) DO NOTHING;\");\n    }\n\n    public static void createSchema(JdbcTemplate jdbcTemplate) {\n        jdbcTemplate.execute(\"Create Table IF NOT EXISTS products (id varchar(40) primary key, name varchar(40));\");\n    }\n}\n"
  },
  {
    "path": "modern-best-practices-testing-java/src/main/java/com/phauer/modernunittesting/TaxServiceClient.java",
    "content": "package com.phauer.modernunittesting;\n\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class TaxServiceClient {\n    public TaxServiceClient() {\n\n    }\n\n    public TaxServiceClient(String url) {\n\n    }\n}\n"
  },
  {
    "path": "modern-best-practices-testing-java/src/main/java/com/phauer/modernunittesting/TaxServiceResponseDTO.java",
    "content": "package com.phauer.modernunittesting;\n\nimport java.util.Locale;\n\npublic class TaxServiceResponseDTO {\n\n    private String locale;\n    private double rate;\n\n    public TaxServiceResponseDTO(Locale germany, double rate) {\n\n    }\n\n    public String getLocale() {\n        return locale;\n    }\n\n    public TaxServiceResponseDTO setLocale(String locale) {\n        this.locale = locale;\n        return this;\n    }\n\n    public double getRate() {\n        return rate;\n    }\n\n    public TaxServiceResponseDTO setRate(double rate) {\n        this.rate = rate;\n        return this;\n    }\n}\n"
  },
  {
    "path": "modern-best-practices-testing-java/src/main/resources/application.properties",
    "content": "spring.datasource.driverClassName=org.postgresql.Driver\nspring.datasource.url=jdbc:postgresql://localhost:5432/\nspring.datasource.username=postgres\nspring.datasource.password=password"
  },
  {
    "path": "modern-best-practices-testing-java/src/test/java/com/phauer/modernunittesting/AssertJTest.java",
    "content": "package com.phauer.modernunittesting;\n\n\nimport org.junit.jupiter.api.Test;\n\nimport java.time.Instant;\nimport java.util.ArrayList;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\npublic class AssertJTest {\n\n    @Test\n    public void bla() {\n        var instant1 = Instant.now();\n        var instant2 = Instant.now();\n        var actualProductList = new ArrayList<Product>();\n        actualProductList.add(new Product(1, \"Samsung Galaxy\", \"Smartphone\"));\n        var actualProduct = new Product(1, \"Samsung\", \"Smartphone\");\n        var expectedProduct = new Product(2, \"iPhone\", \"Smartphone\");\n        var expectedProduct1 = new Product();\n        var expectedProduct2 = new Product();\n\n        assertThat(actualProductList).containsExactly(\n                createProductDTO(\"1\", \"Smartphone\", 250.00),\n                createProductDTO(\"1\", \"Smartphone\", 250.00)\n        );\n\n        assertThat(actualProductList)\n                .anySatisfy(product -> assertThat(product.getDateCreated()).isBetween(instant1, instant2));\n\n        assertThat(actualProduct)\n                .isEqualToIgnoringGivenFields(expectedProduct, \"id\");\n\n        assertThat(actualProductList)\n                .usingElementComparatorIgnoringFields(\"id\")\n                .containsExactly(expectedProduct1, expectedProduct2);\n\n        assertThat(actualProductList)\n                .extracting(Product::getId)\n                .containsExactly(1, 2);\n\n        assertThat(actualProductList)\n                .filteredOn(product -> product.getCategory().equals(\"Smartphone\"))\n                .extracting(Product::getId)\n                .containsOnly(1, 2);\n\n        assertThat(actualProductList)\n                .anySatisfy(product -> {\n                    assertThat(product.getCategory()).isEqualTo(\"Smartphone\");\n                    assertThat(product.isLiked()).isTrue();\n                });\n\n        assertThat(actualProductList)\n                .filteredOn(product -> product.getCategory().equals(\"Smartphone\"))\n                .allSatisfy(product -> assertThat(product.isLiked()).isTrue());\n\n        actualProductList.get(0).setDateCreated(Instant.now());\n        assertThat(actualProductList.get(0).getDateCreated()).isBetween(instant1, instant2);\n\n        // Don't\n        assertTrue(actualProductList.contains(expectedProduct));\n        assertTrue(actualProductList.size() == 5);\n        assertTrue(actualProduct instanceof Product);\n\n        // Do\n        assertThat(actualProductList).contains(expectedProduct);\n        assertThat(actualProductList).hasSize(5);\n        assertThat(actualProduct).isInstanceOf(Product.class);\n    }\n\n    private Product createProductDTO(String s, String smartphone, double v) {\n        return new Product();\n    }\n}\n"
  },
  {
    "path": "modern-best-practices-testing-java/src/test/java/com/phauer/modernunittesting/AwaitilityTest.java",
    "content": "package com.phauer.modernunittesting;\n\nimport org.awaitility.core.ConditionFactory;\nimport org.junit.jupiter.api.Test;\n\nimport java.time.Duration;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.awaitility.Awaitility.await;\n\npublic class AwaitilityTest {\n    private static final ConditionFactory WAIT = await()\n            .atMost(Duration.ofSeconds(6))\n            .pollInterval(Duration.ofSeconds(1))\n            .pollDelay(Duration.ofSeconds(1));\n\n    @Test\n    public void waitAndPoll() {\n        triggerAsyncEvent();\n        WAIT.untilAsserted(() -> {\n            assertThat(findInDatabase(1).getState()).isEqualTo(State.SUCCESS);\n        });\n    }\n\n    private void triggerAsyncEvent() {\n\n    }\n\n    private Thread findInDatabase(int i) {\n        return null;\n    }\n\n    enum State {\n        SUCCESS\n    }\n}\n"
  },
  {
    "path": "modern-best-practices-testing-java/src/test/java/com/phauer/modernunittesting/DesignControllerTest.java",
    "content": "package com.phauer.modernunittesting;\n\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\n\npublic class DesignControllerTest {\n    @Nested\n    class GetDesigns {\n        @Test\n        public void allFieldsAreIncluded() {\n        }\n\n        @Test\n        public void limitParameter() {\n        }\n\n        @Test\n        public void filterParameter() {\n        }\n    }\n\n    @Nested\n    class DeleteDesign {\n        @Test\n        public void designIsRemovedFromDb() {\n        }\n\n        @Test\n        public void return404OnInvalidIdParameter() {\n        }\n\n        @Test\n        public void return401IfNotAuthorized() {\n        }\n    }\n}\n"
  },
  {
    "path": "modern-best-practices-testing-java/src/test/java/com/phauer/modernunittesting/DisplayNameTest.java",
    "content": "package com.phauer.modernunittesting;\n\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Test;\n\npublic class DisplayNameTest {\n    @Test\n    @DisplayName(\"Design is removed from database\")\n    void designIsRemoved() {\n    }\n\n    @Test\n    @DisplayName(\"Return 404 in case of an invalid parameter\")\n    void return404() {\n    }\n\n    @Test\n    @DisplayName(\"Return 401 if the request is not authorized\")\n    void return401() {\n    }\n}\n\n\n//class DesignControllerTest {\n//    @Test\n//    fun `design is removed from db`() {\n//    }\n//    @Test\n//    fun `return 404 on invalid id parameter`() {\n//    }\n//\n//    @Test\n//    fun `return 401 if not authorized`() {\n//    }\n//}\n"
  },
  {
    "path": "modern-best-practices-testing-java/src/test/java/com/phauer/modernunittesting/HelperFunctions.java",
    "content": "package com.phauer.modernunittesting;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.core.type.TypeReference;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport okhttp3.mockwebserver.MockWebServer;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.springframework.boot.jdbc.DataSourceBuilder;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.servlet.view.InternalResourceViewResolver;\nimport org.testcontainers.containers.PostgreSQLContainer;\n\nimport javax.sql.DataSource;\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\nimport java.net.Socket;\nimport java.util.List;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\npublic class HelperFunctions {\n\n    private MockWebServer taxService;\n    private JdbcTemplate template;\n    private MockMvc client;\n\n    @BeforeAll\n    public void setup() throws IOException {\n//        DataSource dataSource = createDataSourceAndStartDatabaseIfNecessary();\n\n        // ProductDAO\n        PostgreSQLContainer db = new PostgreSQLContainer(\"postgres:11.2-alpine\");\n        db.start();\n        DataSource dataSource = DataSourceBuilder.create()\n                .driverClassName(\"org.postgresql.Driver\")\n                .username(db.getUsername())\n                .password(db.getPassword())\n                .url(db.getJdbcUrl())\n                .build();\n        this.template = new JdbcTemplate(dataSource);\n        SchemaCreator.createSchema(template);\n        ProductDAO dao = new ProductDAO(template);\n\n        // TaxServiceClient\n        this.taxService = new MockWebServer();\n        taxService.start();\n        TaxServiceClient client = new TaxServiceClient(taxService.url(\"\").toString());\n\n        // PriceCalculator\n        PriceCalculator calculator = new PriceCalculator();\n\n        // ProductController\n        ProductController controller = new ProductController(dao, client, calculator);\n        this.client = MockMvcBuilders.standaloneSetup(controller).setViewResolvers(new InternalResourceViewResolver()).build();\n    }\n\n    // Don't\n    @Test\n    public void categoryQueryParameter() throws Exception {\n        List<ProductEntity> products = List.of(\n                new ProductEntity().setId(\"11\").setName(\"Envelope\").setCategory(\"Office\").setDescription(\"An Envelope\").setStockAmount(1),\n                new ProductEntity().setId(\"22\").setName(\"Pen\").setCategory(\"Office\").setDescription(\"A Pen\").setStockAmount(1),\n                new ProductEntity().setId(\"33\").setName(\"Notebook\").setCategory(\"Hardware\").setDescription(\"A Notebook\").setStockAmount(2)\n        );\n        for (ProductEntity product : products) {\n            template.execute(createSqlInsertStatement(product));\n        }\n\n        String responseJson = client.perform(get(\"/products?category=Office\"))\n                .andExpect(status().is(200))\n                .andReturn().getResponse().getContentAsString();\n\n        assertThat(toDTOs(responseJson))\n                .extracting(ProductDTO::getId)\n                .containsOnly(\"11\", \"22\");\n    }\n\n\n    // Do\n    @Test\n    public void categoryQueryParameter2() throws Exception {\n        insertIntoDatabase(\n                createProductWithCategory(\"11\", \"Office\"),\n                createProductWithCategory(\"22\", \"Office\"),\n                createProductWithCategory(\"33\", \"Hardware\")\n        );\n\n        String responseJson = requestProductsByCategory(\"Office\");\n\n        assertThat(toDTOs(responseJson))\n                .extracting(ProductDTO::getId)\n                .containsOnly(\"11\", \"22\");\n    }\n\n    private String createSqlInsertStatement(ProductEntity product) {\n        return \"insert into products(id, name) values ('\" + product.getId() + \"', '\" + product.getName() + \"');\";\n    }\n\n    private ProductEntity createProductWithCategory(String id, String category) {\n        return new ProductEntity().setId(id).setName(\"Envelope\").setCategory(category).setDescription(\"An Envelope\").setStockAmount(1);\n    }\n\n    private String requestProductsByCategory(String category) throws Exception {\n        return client.perform(get(\"/products?category=\" + category))\n                .andExpect(status().is(200))\n                .andReturn().getResponse().getContentAsString();\n    }\n\n    private List<ProductDTO> toDTOs(String string) throws IOException {\n        TypeReference dtoType = new TypeReference<List<ProductDTO>>() {\n        };\n        return new ObjectMapper().readValue(string, dtoType);\n    }\n\n    private void insertIntoDatabase(ProductEntity... products) {\n        for (ProductEntity product : products) {\n            template.execute(\"insert into products(id, name) values ('\" + product.getId() + \"', '\" + product.getName() + \"');\");\n        }\n    }\n\n    private String toJson(TaxServiceResponseDTO taxServiceResponseDTO) throws JsonProcessingException {\n        return new ObjectMapper().writeValueAsString(taxServiceResponseDTO);\n    }\n\n    private DataSource createDataSourceAndStartDatabaseIfNecessary() {\n        DataSourceBuilder<?> builder = DataSourceBuilder.create().driverClassName(\"org.postgresql.Driver\");\n        try {\n            // e.g. if started once via `docker-compose up`. see docker-compose.yml.\n            Socket socket = new Socket();\n            socket.connect(new InetSocketAddress(\"localhost\", 5432), 100);\n            socket.close();\n            return builder.username(\"postgres\").password(\"password\")\n                    .url(\"jdbc:postgresql://localhost:5432/\")\n                    .build();\n        } catch (Exception ex) {\n            PostgreSQLContainer db = new PostgreSQLContainer(\"postgres:11.2-alpine\");\n            db.start();\n            return builder.username(db.getUsername()).password(db.getPassword())\n                    .url(db.getJdbcUrl())\n                    .build();\n        }\n    }\n\n    @BeforeEach\n    public void beforeEach() {\n        template.execute(\"truncate table products\");\n    }\n}"
  },
  {
    "path": "modern-best-practices-testing-java/src/test/java/com/phauer/modernunittesting/ParameterTest.java",
    "content": "package com.phauer.modernunittesting;\n\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.CsvSource;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class ParameterTest {\n\n    private Calculator calculator = new Calculator();\n\n    @ParameterizedTest\n    @CsvSource({\n            \"1, 1, 2\",\n            \"5, 3, 8\",\n            \"10, -20, -10\"\n    })\n    public void add(int summand1, int summand2, int expectedSum) {\n        assertThat(calculator.add(summand1, summand2)).isEqualTo(expectedSum);\n    }\n}\n\nclass Calculator {\n\n    public int add(int summand1, int summand2) {\n        return summand1 + summand2;\n    }\n}\n\n"
  },
  {
    "path": "modern-best-practices-testing-java/src/test/java/com/phauer/modernunittesting/Product.java",
    "content": "package com.phauer.modernunittesting;\n\nimport java.time.Instant;\nimport java.util.Objects;\nimport java.util.StringJoiner;\n\npublic class Product {\n\n    private int id;\n    private String name;\n    private String category;\n    private Instant dateCreated;\n    private boolean liked;\n\n    public Product() {\n\n    }\n\n    public Product(int id, String name, String category) {\n\n        this.id = id;\n        this.name = name;\n        this.category = category;\n    }\n\n    public int getId() {\n        return id;\n    }\n\n    public Product setId(int id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public Product setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public String getCategory() {\n        return category;\n    }\n\n    public Product setCategory(String category) {\n        this.category = category;\n        return this;\n    }\n\n    public Instant getDateCreated() {\n        return dateCreated;\n    }\n\n    public Product setDateCreated(Instant dateCreated) {\n        this.dateCreated = dateCreated;\n        return this;\n    }\n\n    public boolean isLiked() {\n        return liked;\n    }\n\n    public Product setLiked(boolean liked) {\n        this.liked = liked;\n        return this;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        Product product = (Product) o;\n        return Objects.equals(id, product.id) &&\n                Objects.equals(dateCreated, product.dateCreated);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(id, dateCreated);\n    }\n\n    @Override\n    public String toString() {\n        return new StringJoiner(\", \", Product.class.getSimpleName() + \"[\", \"]\")\n                .add(\"id=\" + id)\n                .add(\"name='\" + name + \"'\")\n                .toString();\n    }\n}\n"
  },
  {
    "path": "modern-best-practices-testing-java/src/test/java/com/phauer/modernunittesting/ProductControllerITest.java",
    "content": "package com.phauer.modernunittesting;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.core.type.TypeReference;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.springframework.boot.jdbc.DataSourceBuilder;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.servlet.view.InternalResourceViewResolver;\nimport org.testcontainers.containers.PostgreSQLContainer;\n\nimport javax.sql.DataSource;\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\nimport java.net.Socket;\nimport java.util.List;\nimport java.util.Locale;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\npublic class ProductControllerITest {\n\n    private MockWebServer taxService;\n    private JdbcTemplate template;\n    private MockMvc client;\n\n    @BeforeAll\n    public void setup() throws IOException {\n//        DataSource dataSource = createDataSourceAndStartDatabaseIfNecessary();\n\n        // ProductDAO\n        PostgreSQLContainer db = new PostgreSQLContainer(\"postgres:11.2-alpine\");\n        db.start();\n        DataSource dataSource = DataSourceBuilder.create()\n                .driverClassName(\"org.postgresql.Driver\")\n                .username(db.getUsername())\n                .password(db.getPassword())\n                .url(db.getJdbcUrl())\n                .build();\n        this.template = new JdbcTemplate(dataSource);\n        SchemaCreator.createSchema(template);\n        ProductDAO dao = new ProductDAO(template);\n\n        // TaxServiceClient\n        this.taxService = new MockWebServer();\n        taxService.start();\n        TaxServiceClient client = new TaxServiceClient(taxService.url(\"\").toString());\n\n        // PriceCalculator\n        PriceCalculator calculator = new PriceCalculator();\n\n        // ProductController\n        ProductController controller = new ProductController(dao, client, calculator);\n        this.client = MockMvcBuilders.standaloneSetup(controller).setViewResolvers(new InternalResourceViewResolver()).build();\n    }\n\n    @Test\n    public void databaseDataIsCorrectlyReturned() throws Exception {\n        insertIntoDatabase(\n                new ProductEntity().setId(\"90\").setName(\"Envelope\"),\n                new ProductEntity().setId(\"50\").setName(\"Pen\")\n        );\n        taxService.enqueue(new MockResponse()\n                .setResponseCode(200)\n                .setBody(toJson(new TaxServiceResponseDTO(Locale.GERMANY, 0.19)))\n        );\n\n        String responseJson = client.perform(get(\"/products\").param(\"token\", \"asdf\"))\n                .andExpect(status().is(200))\n                .andReturn().getResponse().getContentAsString();\n\n        assertThat(toDTOs(responseJson)).containsOnly(\n                new ProductDTO().setId(\"90\").setName(\"Envelope\").setPrice(0.5),\n                new ProductDTO().setId(\"50\").setName(\"Pen\").setPrice(0.5)\n        );\n        // or assert the data in the database if the request should change something.\n    }\n\n    private List<ProductDTO> toDTOs(String string) throws IOException {\n        TypeReference dtoType = new TypeReference<List<ProductDTO>>() {\n        };\n        return new ObjectMapper().readValue(string, dtoType);\n    }\n\n    private void insertIntoDatabase(ProductEntity... products) {\n        List<ProductEntity> products2 = List.of(new ProductEntity());\n        for (ProductEntity product : products) {\n            template.execute(\"insert into products(id, name) values ('\" + product.getId() + \"', '\" + product.getName() + \"');\");\n        }\n    }\n\n    private String toJson(TaxServiceResponseDTO taxServiceResponseDTO) throws JsonProcessingException {\n        return new ObjectMapper().writeValueAsString(taxServiceResponseDTO);\n    }\n\n    private DataSource createDataSourceAndStartDatabaseIfNecessary() {\n        DataSourceBuilder<?> builder = DataSourceBuilder.create().driverClassName(\"org.postgresql.Driver\");\n        try {\n            // e.g. if started once via `docker-compose up`. see docker-compose.yml.\n            Socket socket = new Socket();\n            socket.connect(new InetSocketAddress(\"localhost\", 5432), 100);\n            socket.close();\n            return builder.username(\"postgres\").password(\"password\")\n                    .url(\"jdbc:postgresql://localhost:5432/\")\n                    .build();\n        } catch (Exception ex) {\n            PostgreSQLContainer db = new PostgreSQLContainer(\"postgres:11.2-alpine\");\n            db.start();\n            return builder.username(db.getUsername()).password(db.getPassword())\n                    .url(db.getJdbcUrl())\n                    .build();\n        }\n    }\n\n    @BeforeEach\n    public void beforeEach() {\n        template.execute(\"truncate table products\");\n    }\n}"
  },
  {
    "path": "modern-best-practices-testing-java/src/test/java/com/phauer/modernunittesting/ProductControllerITest2.java",
    "content": "package com.phauer.modernunittesting;\n\nimport org.junit.jupiter.api.Test;\nimport org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;\n\n@WebMvcTest(ProductController.class)\nclass ProductControllerITest2 {\n\n    @Test\n    public void foo() {\n\n    }\n\n}"
  },
  {
    "path": "modern-best-practices-testing-java/src/test/java/com/phauer/modernunittesting/ProductViewITest.java",
    "content": "package com.phauer.modernunittesting;\n\nimport com.github.mvysny.kaributesting.v10.GridKt;\nimport com.github.mvysny.kaributesting.v10.MockVaadin;\nimport com.vaadin.flow.component.button.Button;\nimport com.vaadin.flow.component.grid.Grid;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.springframework.boot.jdbc.DataSourceBuilder;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.testcontainers.containers.PostgreSQLContainer;\n\nimport javax.sql.DataSource;\n\nimport static com.github.mvysny.kaributesting.v10.LocatorJ._click;\nimport static com.github.mvysny.kaributesting.v10.LocatorJ._get;\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\nclass ProductViewITest {\n\n    private JdbcTemplate template;\n    private ProductView view;\n\n    @BeforeAll\n    public void beforeAll() {\n        MockVaadin.setup();\n        PostgreSQLContainer db = new PostgreSQLContainer(\"postgres:11.2-alpine\");\n        db.start();\n        DataSource dataSource = DataSourceBuilder.create()\n                .driverClassName(\"org.postgresql.Driver\")\n                .username(db.getUsername())\n                .password(db.getPassword())\n                .url(db.getJdbcUrl())\n                .build();\n        template = new JdbcTemplate(dataSource);\n        SchemaCreator.createSchema(template);\n    }\n\n    @BeforeEach\n    public void beforeEach() {\n        ProductDAO dao = new ProductDAO(template);\n        view = new ProductView(dao);\n    }\n\n    @Test\n    public void prodcutsAreCorrectlyDisplayedInTable() {\n        insertIntoDatabase(\n                new ProductEntity().setId(\"90\").setName(\"Envelope\"),\n                new ProductEntity().setId(\"50\").setName(\"Pen\")\n        );\n\n        Button button = _get(view, Button.class, spec -> spec.withText(\"Load Products\"));\n        _click(button);\n\n        Grid<ProductModel> grid = _get(view, Grid.class);\n        assertThat(GridKt._size(grid)).isEqualTo(2);\n        assertThat(GridKt._get(grid, 0))\n                .isEqualTo(new ProductModel().setId(\"90\").setName(\"Envelope\"));\n    }\n\n    private void insertIntoDatabase(ProductEntity... products) {\n        for (ProductEntity product : products) {\n            template.execute(\"insert into products(id, name) values ('\" + product.getId() + \"', '\" + product.getName() + \"');\");\n        }\n    }\n}"
  },
  {
    "path": "modern-best-practices-testing-java/src/test/java/com/phauer/modernunittesting/RandomizedValues.java",
    "content": "package com.phauer.modernunittesting;\n\nimport org.bson.types.ObjectId;\nimport org.junit.jupiter.api.Test;\n\nimport java.time.Instant;\nimport java.util.Random;\nimport java.util.UUID;\n\npublic class RandomizedValues {\n\n    @Test\n    public void randomized() {\n        // Don't\n        Instant ts1 = Instant.now(); // 1557582788\n        Instant ts2 = ts1.plusSeconds(1); // 1557582789\n        ObjectId generatedId = new ObjectId(); // 5cd6d3c469974112f4be30d2\n        int randomAmount = new Random().nextInt(500); // 232\n        UUID uuid = UUID.randomUUID(); // d5d1f61b-0a8b-42be-b05a-bd458bb563ad\n    }\n\n    @Test\n    public void fixed() {\n        // Do\n        Instant ts1 = Instant.ofEpochSecond(1550000001);\n        Instant ts2 = Instant.ofEpochSecond(1550000002);\n        ObjectId id1 = new ObjectId(\"000000000000000000000001\");\n        int amount = 50;\n        UUID uuid = UUID.fromString(\"00000000-000-0000-0000-000000000000\");\n    }\n}\n"
  },
  {
    "path": "modern-integration-testing/.gitignore",
    "content": "HELP.md\n/target/\n!.mvn/wrapper/maven-wrapper.jar\n\n### STS ###\n.apt_generated\n.classpath\n.factorypath\n.project\n.settings\n.springBeans\n.sts4-cache\n\n### IntelliJ IDEA ###\n.idea\n*.iws\n*.iml\n*.ipr\n\n### NetBeans ###\n/nbproject/private/\n/nbbuild/\n/dist/\n/nbdist/\n/.nb-gradle/\n/build/\n"
  },
  {
    "path": "modern-integration-testing/docker-compose.yml",
    "content": "version: '3.1'\nservices:\n  db:\n    image: \"postgres:11.2-alpine\"\n    environment:\n      POSTGRES_PASSWORD: password\n      # user: postgres\n      # pw: password\n    ports:\n      - \"5432:5432\"\n  adminer:\n    image: adminer\n    restart: always\n    ports:\n      - \"90:8080\"\n"
  },
  {
    "path": "modern-integration-testing/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>com.phauer</groupId>\n    <artifactId>modern-integration-testing</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>11</java.version>\n        <vaadin.version>13.0.0</vaadin.version>\n        <testcontainers.version>1.10.7</testcontainers.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-jdbc</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.vaadin</groupId>\n            <artifactId>vaadin-spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.postgresql</groupId>\n            <artifactId>postgresql</artifactId>\n            <version>42.2.5</version>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n            <exclusions>\n                <exclusion>\n                    <groupId>junit</groupId>\n                    <artifactId>junit</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n        <dependency>\n            <groupId>org.assertj</groupId>\n            <artifactId>assertj-core</artifactId>\n            <version>3.11.1</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.junit.jupiter</groupId>\n            <artifactId>junit-jupiter</artifactId>\n            <version>5.4.0</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.testcontainers</groupId>\n            <artifactId>testcontainers</artifactId>\n            <version>${testcontainers.version}</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.testcontainers</groupId>\n            <artifactId>postgresql</artifactId>\n            <version>${testcontainers.version}</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.squareup.okhttp3</groupId>\n            <artifactId>mockwebserver</artifactId>\n            <version>3.13.1</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.github.mvysny.kaributesting</groupId>\n            <artifactId>karibu-testing-v10</artifactId>\n            <version>1.1.4</version>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>com.vaadin</groupId>\n                <artifactId>vaadin-bom</artifactId>\n                <version>${vaadin.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "modern-integration-testing/src/main/java/com/phauer/modernunittesting/MainLayout.java",
    "content": "package com.phauer.modernunittesting;\n\nimport com.vaadin.flow.component.html.Div;\nimport com.vaadin.flow.component.page.Push;\nimport com.vaadin.flow.component.page.Viewport;\nimport com.vaadin.flow.router.RouterLayout;\n\n@Push\n@Viewport(\"width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes\")\npublic class MainLayout extends Div implements RouterLayout {\n}\n"
  },
  {
    "path": "modern-integration-testing/src/main/java/com/phauer/modernunittesting/ModernUnitTestingApplication.java",
    "content": "package com.phauer.modernunittesting;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ModernUnitTestingApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ModernUnitTestingApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "modern-integration-testing/src/main/java/com/phauer/modernunittesting/PriceCalculator.java",
    "content": "package com.phauer.modernunittesting;\n\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class PriceCalculator {\n}\n"
  },
  {
    "path": "modern-integration-testing/src/main/java/com/phauer/modernunittesting/ProductController.java",
    "content": "package com.phauer.modernunittesting;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n@RestController\npublic class ProductController {\n\n    private final ProductDAO dao;\n\n    public ProductController(ProductDAO dao, TaxServiceClient client, PriceCalculator calculator) {\n        this.dao = dao;\n    }\n\n    @GetMapping(\"/products\")\n    public List<ProductDTO> getProducts() {\n        List<ProductEntity> products = dao.findProducts();\n        return toDto(products);\n    }\n\n    private List<ProductDTO> toDto(List<ProductEntity> products) {\n        return products.stream()\n                .map(this::toDto)\n                .collect(Collectors.toList());\n    }\n\n    private ProductDTO toDto(ProductEntity entity) {\n        return new ProductDTO()\n                .setId(entity.getId())\n                .setName(entity.getName())\n                .setPrice(0.5);\n    }\n}\n"
  },
  {
    "path": "modern-integration-testing/src/main/java/com/phauer/modernunittesting/ProductDAO.java",
    "content": "package com.phauer.modernunittesting;\n\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.stereotype.Component;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.List;\n\n@Component\npublic class ProductDAO {\n\n    private final JdbcTemplate template;\n\n    public ProductDAO(JdbcTemplate template) {\n        this.template = template;\n    }\n\n    public List<ProductEntity> findProducts() {\n        return template.query(\"select * from products;\", this::map);\n    }\n\n    private ProductEntity map(ResultSet resultSet, int i) throws SQLException {\n        return new ProductEntity()\n                .setId(resultSet.getString(\"id\"))\n                .setName(resultSet.getString(\"name\"));\n    }\n\n}\n\n\n"
  },
  {
    "path": "modern-integration-testing/src/main/java/com/phauer/modernunittesting/ProductDTO.java",
    "content": "package com.phauer.modernunittesting;\n\nimport java.util.Objects;\nimport java.util.StringJoiner;\n\npublic class ProductDTO {\n    private String id;\n    private String name;\n    private double price;\n\n    public double getPrice() {\n        return price;\n    }\n\n    public ProductDTO setPrice(double price) {\n        this.price = price;\n        return this;\n    }\n\n    public String getId() {\n        return id;\n    }\n\n    public ProductDTO setId(String id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public ProductDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return new StringJoiner(\", \", ProductDTO.class.getSimpleName() + \"[\", \"]\")\n                .add(\"id='\" + id + \"'\")\n                .add(\"name='\" + name + \"'\")\n                .add(\"price=\" + price)\n                .toString();\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        ProductDTO that = (ProductDTO) o;\n        return Double.compare(that.price, price) == 0 &&\n                Objects.equals(id, that.id) &&\n                Objects.equals(name, that.name);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(id, name, price);\n    }\n}\n"
  },
  {
    "path": "modern-integration-testing/src/main/java/com/phauer/modernunittesting/ProductEntity.java",
    "content": "package com.phauer.modernunittesting;\n\nimport java.util.Objects;\nimport java.util.StringJoiner;\n\npublic class ProductEntity {\n    private String id;\n    private String name;\n\n    public String getId() {\n        return id;\n    }\n\n    public ProductEntity setId(String id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public ProductEntity setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        ProductEntity that = (ProductEntity) o;\n        return Objects.equals(id, that.id) &&\n                Objects.equals(name, that.name);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(id, name);\n    }\n\n    @Override\n    public String toString() {\n        return new StringJoiner(\", \", ProductEntity.class.getSimpleName() + \"[\", \"]\")\n                .add(\"id='\" + id + \"'\")\n                .add(\"name='\" + name + \"'\")\n                .toString();\n    }\n}\n"
  },
  {
    "path": "modern-integration-testing/src/main/java/com/phauer/modernunittesting/ProductModel.java",
    "content": "package com.phauer.modernunittesting;\n\nimport java.util.Objects;\nimport java.util.StringJoiner;\n\npublic class ProductModel {\n    private String id;\n    private String name;\n\n    public String getId() {\n        return id;\n    }\n\n    public ProductModel setId(String id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public ProductModel setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        ProductModel that = (ProductModel) o;\n        return Objects.equals(id, that.id) &&\n                Objects.equals(name, that.name);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(id, name);\n    }\n\n    @Override\n    public String toString() {\n        return new StringJoiner(\", \", ProductModel.class.getSimpleName() + \"[\", \"]\")\n                .add(\"id='\" + id + \"'\")\n                .add(\"name='\" + name + \"'\")\n                .toString();\n    }\n}\n"
  },
  {
    "path": "modern-integration-testing/src/main/java/com/phauer/modernunittesting/ProductView.java",
    "content": "package com.phauer.modernunittesting;\n\nimport com.vaadin.flow.component.ClickEvent;\nimport com.vaadin.flow.component.button.Button;\nimport com.vaadin.flow.component.grid.Grid;\nimport com.vaadin.flow.component.orderedlayout.VerticalLayout;\nimport com.vaadin.flow.router.PageTitle;\nimport com.vaadin.flow.router.Route;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n@Route(value = \"\", layout = MainLayout.class)\n@PageTitle(\"Hello Vaadin\")\npublic class ProductView extends VerticalLayout {\n\n    private ProductDAO dao;\n\n    private Grid<ProductModel> grid;\n\n    public ProductView(ProductDAO dao) {\n        this.dao = dao;\n        initView();\n    }\n\n    private void initView() {\n        Button button = new Button(\"Load Products\");\n        button.addClickListener(this::loadButtonHandler);\n\n        this.grid = new Grid<>(ProductModel.class);\n\n        add(button, grid);\n    }\n\n    private void loadButtonHandler(ClickEvent event) {\n        var products = dao.findProducts();\n        grid.setItems(toModel(products));\n    }\n\n    private List<ProductModel> toModel(List<ProductEntity> products) {\n        return products.stream()\n                .map(this::toModel)\n                .collect(Collectors.toList());\n    }\n\n    private ProductModel toModel(ProductEntity entity) {\n        return new ProductModel()\n                .setId(entity.getId())\n                .setName(entity.getName());\n    }\n}\n"
  },
  {
    "path": "modern-integration-testing/src/main/java/com/phauer/modernunittesting/SchemaCreator.java",
    "content": "package com.phauer.modernunittesting;\n\nimport org.springframework.boot.ApplicationArguments;\nimport org.springframework.boot.ApplicationRunner;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class SchemaCreator implements ApplicationRunner {\n\n    private final JdbcTemplate jdbcTemplate;\n\n    public SchemaCreator(JdbcTemplate jdbcTemplate) {\n        this.jdbcTemplate = jdbcTemplate;\n    }\n\n    @Override\n    public void run(ApplicationArguments args) throws Exception {\n        createSchema(jdbcTemplate);\n\n        jdbcTemplate.execute(\"insert into products(id, name) values \" +\n                \"('1', 'notebook'), \" +\n                \"('2', 'smartphone'), \" +\n                \"('3', 'cup') \" +\n                \"ON CONFLICT(id) DO NOTHING;\");\n    }\n\n    public static void createSchema(JdbcTemplate jdbcTemplate) {\n        jdbcTemplate.execute(\"Create Table IF NOT EXISTS products (id varchar(40) primary key, name varchar(40));\");\n    }\n}\n"
  },
  {
    "path": "modern-integration-testing/src/main/java/com/phauer/modernunittesting/TaxServiceClient.java",
    "content": "package com.phauer.modernunittesting;\n\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class TaxServiceClient {\n    public TaxServiceClient() {\n\n    }\n    public TaxServiceClient(String url) {\n\n    }\n}\n"
  },
  {
    "path": "modern-integration-testing/src/main/java/com/phauer/modernunittesting/TaxServiceResponseDTO.java",
    "content": "package com.phauer.modernunittesting;\n\nimport java.util.Locale;\n\npublic class TaxServiceResponseDTO {\n\n    private String locale;\n    private double rate;\n\n    public TaxServiceResponseDTO(Locale germany, double rate) {\n\n    }\n\n    public String getLocale() {\n        return locale;\n    }\n\n    public TaxServiceResponseDTO setLocale(String locale) {\n        this.locale = locale;\n        return this;\n    }\n\n    public double getRate() {\n        return rate;\n    }\n\n    public TaxServiceResponseDTO setRate(double rate) {\n        this.rate = rate;\n        return this;\n    }\n}\n"
  },
  {
    "path": "modern-integration-testing/src/main/resources/application.properties",
    "content": "spring.datasource.driverClassName=org.postgresql.Driver\nspring.datasource.url=jdbc:postgresql://localhost:5432/\nspring.datasource.username=postgres\nspring.datasource.password=password"
  },
  {
    "path": "modern-integration-testing/src/test/java/com/phauer/modernunittesting/AssertJTest.java",
    "content": "package com.phauer.modernunittesting;\n\n\nimport org.junit.jupiter.api.Test;\n\nimport java.time.Instant;\nimport java.util.ArrayList;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class AssertJTest {\n\n    @Test\n    public void bla(){\n        var instant1 = Instant.now();\n        var instant2 = Instant.now();\n        var actualProductList = new ArrayList<Product>();\n        var actualProduct = new Product();\n        var expectedProduct = new Product();\n        var expectedProduct1 = new Product();\n        var expectedProduct2 = new Product();\n\n        assertThat(actualProductList).containsExactly(\n                createProductDTO(\"1\", \"Smartphone\", 250.00),\n                createProductDTO(\"1\", \"Smartphone\", 250.00)\n        );\n\n        assertThat(actualProductList).anySatisfy(product -> {\n            assertThat(product.getDateCreated()).isBetween(instant1, instant2);\n        });\n\n        assertThat(actualProduct)\n                .isEqualToIgnoringGivenFields(expectedProduct, \"id\");\n\n        assertThat(actualProductList)\n                .usingElementComparatorIgnoringFields(\"id\")\n                .containsExactly(expectedProduct1, expectedProduct2);\n\n        assertThat(actualProductList)\n                .extracting(Product::getId)\n                .containsExactly(\"1\", \"2\");\n    }\n\n    private Product createProductDTO(String s, String smartphone, double v) {\n        return new Product();\n    }\n}\n"
  },
  {
    "path": "modern-integration-testing/src/test/java/com/phauer/modernunittesting/Product.java",
    "content": "package com.phauer.modernunittesting;\n\nimport java.time.Instant;\nimport java.util.Objects;\n\npublic class Product {\n\n    private String id;\n    private Instant dateCreated;\n\n    public String getId() {\n        return id;\n    }\n\n    public Product setId(String id) {\n        this.id = id;\n        return this;\n    }\n\n    public Instant getDateCreated() {\n        return dateCreated;\n    }\n\n    public Product setDateCreated(Instant dateCreated) {\n        this.dateCreated = dateCreated;\n        return this;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        Product product = (Product) o;\n        return Objects.equals(id, product.id) &&\n                Objects.equals(dateCreated, product.dateCreated);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(id, dateCreated);\n    }\n}\n"
  },
  {
    "path": "modern-integration-testing/src/test/java/com/phauer/modernunittesting/ProductControllerITest.java",
    "content": "package com.phauer.modernunittesting;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.core.type.TypeReference;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.springframework.boot.jdbc.DataSourceBuilder;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.servlet.view.InternalResourceViewResolver;\nimport org.testcontainers.containers.PostgreSQLContainer;\n\nimport javax.sql.DataSource;\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\nimport java.net.Socket;\nimport java.util.List;\nimport java.util.Locale;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\npublic class ProductControllerITest {\n\n    private MockWebServer taxService;\n    private JdbcTemplate template;\n    private MockMvc client;\n\n    @BeforeAll\n    public void setup() throws IOException {\n//        DataSource dataSource = createDataSourceAndStartDatabaseIfNecessary();\n\n        // ProductDAO\n        PostgreSQLContainer db = new PostgreSQLContainer(\"postgres:11.2-alpine\");\n        db.start();\n        DataSource dataSource = DataSourceBuilder.create()\n                .driverClassName(\"org.postgresql.Driver\")\n                .username(db.getUsername())\n                .password(db.getPassword())\n                .url(db.getJdbcUrl())\n                .build();\n        this.template = new JdbcTemplate(dataSource);\n        SchemaCreator.createSchema(template);\n        ProductDAO dao = new ProductDAO(template);\n\n        // TaxServiceClient\n        this.taxService = new MockWebServer();\n        taxService.start();\n        TaxServiceClient client = new TaxServiceClient(taxService.url(\"\").toString());\n\n        // PriceCalculator\n        PriceCalculator calculator = new PriceCalculator();\n\n        // ProductController\n        ProductController controller = new ProductController(dao, client, calculator);\n        this.client = MockMvcBuilders.standaloneSetup(controller).setViewResolvers(new InternalResourceViewResolver()).build();\n    }\n\n    @Test\n    public void databaseDataIsCorrectlyReturned() throws Exception {\n        insertIntoDatabase(\n                new ProductEntity().setId(\"90\").setName(\"Envelope\"),\n                new ProductEntity().setId(\"50\").setName(\"Pen\")\n        );\n        taxService.enqueue(new MockResponse()\n                .setResponseCode(200)\n                .setBody(toJson(new TaxServiceResponseDTO(Locale.GERMANY, 0.19)))\n        );\n\n        String responseJson = client.perform(get(\"/products\"))\n                .andExpect(status().is(200))\n                .andReturn().getResponse().getContentAsString();\n\n        assertThat(toDTOs(responseJson)).containsOnly(\n                new ProductDTO().setId(\"90\").setName(\"Envelope\").setPrice(0.5),\n                new ProductDTO().setId(\"50\").setName(\"Pen\").setPrice(0.5)\n        );\n        // or assert the data in the database if the request should change something.\n    }\n\n    private List<ProductDTO> toDTOs(String string) throws IOException {\n        TypeReference dtoType = new TypeReference<List<ProductDTO>>() {\n        };\n        return new ObjectMapper().readValue(string, dtoType);\n    }\n\n    private void insertIntoDatabase(ProductEntity... products) {\n        for (ProductEntity product : products) {\n            template.execute(\"insert into products(id, name) values ('\" + product.getId() + \"', '\" + product.getName() + \"');\");\n        }\n    }\n\n    private String toJson(TaxServiceResponseDTO taxServiceResponseDTO) throws JsonProcessingException {\n        return new ObjectMapper().writeValueAsString(taxServiceResponseDTO);\n    }\n\n    private DataSource createDataSourceAndStartDatabaseIfNecessary() {\n        DataSourceBuilder<?> builder = DataSourceBuilder.create().driverClassName(\"org.postgresql.Driver\");\n        try {\n            // e.g. if started once via `docker-compose up`. see docker-compose.yml.\n            Socket socket = new Socket();\n            socket.connect(new InetSocketAddress(\"localhost\", 5432), 100);\n            socket.close();\n            return builder.username(\"postgres\").password(\"password\")\n                    .url(\"jdbc:postgresql://localhost:5432/\")\n                    .build();\n        } catch (Exception ex) {\n            PostgreSQLContainer db = new PostgreSQLContainer(\"postgres:11.2-alpine\");\n            db.start();\n            return builder.username(db.getUsername()).password(db.getPassword())\n                    .url(db.getJdbcUrl())\n                    .build();\n        }\n    }\n\n    @BeforeEach\n    public void beforeEach() {\n        template.execute(\"truncate table products\");\n    }\n}"
  },
  {
    "path": "modern-integration-testing/src/test/java/com/phauer/modernunittesting/ProductControllerITest2.java",
    "content": "package com.phauer.modernunittesting;\n\nimport org.junit.jupiter.api.Test;\nimport org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;\n\n@WebMvcTest(ProductController.class)\nclass ProductControllerITest2 {\n\n    @Test\n    public void foo() {\n\n    }\n\n}"
  },
  {
    "path": "modern-integration-testing/src/test/java/com/phauer/modernunittesting/ProductViewITest.java",
    "content": "package com.phauer.modernunittesting;\n\nimport com.github.mvysny.kaributesting.v10.GridKt;\nimport com.github.mvysny.kaributesting.v10.MockVaadin;\nimport com.vaadin.flow.component.button.Button;\nimport com.vaadin.flow.component.grid.Grid;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.springframework.boot.jdbc.DataSourceBuilder;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.testcontainers.containers.PostgreSQLContainer;\n\nimport javax.sql.DataSource;\n\nimport static com.github.mvysny.kaributesting.v10.LocatorJ._click;\nimport static com.github.mvysny.kaributesting.v10.LocatorJ._get;\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\nclass ProductViewITest {\n\n    private JdbcTemplate template;\n    private ProductView view;\n\n    @BeforeAll\n    public void beforeAll() {\n        MockVaadin.setup();\n        PostgreSQLContainer db = new PostgreSQLContainer(\"postgres:11.2-alpine\");\n        db.start();\n        DataSource dataSource = DataSourceBuilder.create()\n                .driverClassName(\"org.postgresql.Driver\")\n                .username(db.getUsername())\n                .password(db.getPassword())\n                .url(db.getJdbcUrl())\n                .build();\n        template = new JdbcTemplate(dataSource);\n        SchemaCreator.createSchema(template);\n    }\n\n    @BeforeEach\n    public void beforeEach() {\n        ProductDAO dao = new ProductDAO(template);\n        view = new ProductView(dao);\n    }\n\n    @Test\n    public void prodcutsAreCorrectlyDisplayedInTable() {\n        insertIntoDatabase(\n                new ProductEntity().setId(\"90\").setName(\"Envelope\"),\n                new ProductEntity().setId(\"50\").setName(\"Pen\")\n        );\n\n        Button button = _get(view, Button.class, spec -> spec.withText(\"Load Products\"));\n        _click(button);\n\n        Grid<ProductModel> grid = _get(view, Grid.class);\n        assertThat(GridKt._size(grid)).isEqualTo(2);\n        assertThat(GridKt._get(grid, 0))\n                .isEqualTo(new ProductModel().setId(\"90\").setName(\"Envelope\"));\n    }\n\n    private void insertIntoDatabase(ProductEntity... products) {\n        for (ProductEntity product : products) {\n            template.execute(\"insert into products(id, name) values ('\" + product.getId() + \"', '\" + product.getName() + \"');\");\n        }\n    }\n}"
  },
  {
    "path": "mongodb-practice/connection-strings.sh",
    "content": "mongo --host db1.domain.com --port 27017 -u writeUser -p CPy8f82cb productService\nmongo --host db1.domain.com --port 27017 -u readUser -p qBr7bEJSm productService\nmongo --host db2.domain.com --port 27018 -u writeUser -p oFfQdzkKR checkout\nmongo --host db2.domain.com --port 27018 -u readUser -p ehhhTbmeT checkout\nmongo --host db3.domain.com --port 27019 -u writeUser -p LkFnSmNYb shop\nmongo --host db3.domain.com --port 27019 -u readUser -p 4aYLLcgn8 shop\nmongo --host localhost --port 27017"
  },
  {
    "path": "mongodb-practice/docker-compose.yml",
    "content": "version: '3'\nservices:\n  mongo:\n    image: \"mongo:4.0.2\"\n    ports:\n      - \"27017:27017\"\n    command: --profile=1 --slowms=0\n  mongo-seeding:\n    build: ./local-dev/mongo-seeding/\n    depends_on:\n      - mongo\n  mongo-express:\n    image: \"mongo-express:0.49.0\"\n    ports:\n      - \"8081:8081\"\n    depends_on:\n      - mongo"
  },
  {
    "path": "mongodb-practice/example.json",
    "content": "db.products.insert({name: \"Smartphone\", amount: 5, dateCreated: new Date(), isActive: true, tags: [\"a\", \"b\"]})\ndb.products.insert({name: \"Notebook\", amount: 2, dateCreated: new Date(), isActive: false, tags: []})"
  },
  {
    "path": "mongodb-practice/local-dev/mongo-seeding/Dockerfile",
    "content": "FROM python:3.7.2-alpine3.8\n\nRUN pip install --ignore-installed \"pymongo==3.7.2\" \"Faker==1.0.2\"\n\nCOPY seedMongo.py /\nCMD python3 /seedMongo.py\n"
  },
  {
    "path": "mongodb-practice/local-dev/mongo-seeding/seedMongo.py",
    "content": "#!/usr/bin/env python3\nimport random\nfrom datetime import datetime, timedelta\n\nfrom bson import ObjectId\nfrom faker import Faker\nfrom pymongo import MongoClient\n\nfaker = Faker(\"en\")\n\ndef seed():\n    print(\"Start seeding...\")\n    client = MongoClient('mongodb://mongo:27017/test')\n    db = client.test\n    new_products = [generate_product() for _ in range(500)]\n    db.products.delete_many({})\n    db.products.insert_many(new_products)\n    print(\"Finished seeding.\")\n\ndef generate_product():\n    return {\n        \"_id\": ObjectId(),\n        \"name\": faker.company(),\n        \"amount\": random.randrange(100),\n        \"isActive\": faker.boolean(chance_of_getting_true=80),\n        \"dateCreated\": faker.date_time_between(start_date=\"-10y\", end_date=\"now\"),\n        \"tags\": [faker.word(), faker.word()]\n    }\n\nif __name__ == '__main__':\n    seed()\n"
  },
  {
    "path": "python-demo/.gitignore",
    "content": ".idea\n*.iml"
  },
  {
    "path": "python-demo/1concise-powerful.py",
    "content": "# You don’t need {} for method bodies (use indent instead), no semicolon, no () in if conditions.\ndef divide(a, b):\n    if b == 0:\n        raise ValueError(\"Dude, you can't divide {} by {}\".format(a, b))\n    return a / b\n\n# more intuitive than Java's ternary operator\nparameter = \"Peter\"\nname = \"Unknown\" if parameter is None else parameter\n\n# multi-line strings. praise the lord!\ndescription = \"\"\"This is my\nmulti-line string. \"\"\"\n\n# chained comparison\nif 200 <= status_code < 300:\n    print(\"success\")\n"
  },
  {
    "path": "python-demo/2collections.py",
    "content": "# Python is just awesome when it comes to collections\n\n# nice literals for list, tuple, sets, dicts\nmy_list = [1, 2, 3]\nmy_tuple = (1, 2)\nmy_set = {1, 2, 2}\nmy_dict = {\"a\": 1, \"b\": 2}\n# this collection literals makes dealing with JSON a pleasure.\n# this is also the reason why the Python driver for MongoDB makes definitely more fun.\n\n# contains\ncontains_element = 1 in my_list  # True\ncontains_key = \"a\" not in my_dict  # False\n\n# access\nvalue = my_list[0]\nvalue2 = my_dict[\"a\"]\n\n# iteration\nfor element in my_list:\n    print(element)\n\nfor index, element in enumerate(my_list):  # foreach with index!\n    print(index, element)\n\nfor key, value in my_dict.items():  # how cool is that?!\n    print(key, value)\n\n# list can be used as stacks...\nprint(my_list.pop())  # 3 (remove and return element)\n# ...and as queues\nprint(my_list.pop(0))  # 1 (remove and return first element)\n\n# == List Comprehension ==\n# That's my personal kick-ass feature of Python\n# It's like map() and filter() of the Java 8 Stream API, but much better.\n# syntax: [<map_function> for <element> in <collection> if <condition>]\n# example:\nnames = [\"peter\", \"paul\", \"florian\", \"albert\"]\n# filter for names starting with \"p\" (filter()) and upper case them (map())\nresult = [name.upper() for name in names if name.startswith(\"p\")]\nprint(result)  # [\"PETER\", \"PAUL\"]\n# and it returns... a new list! no annoying collect(Collectors.toList()) boilerplate.\n# i really love this powerful and concise syntax.\n\n# slice syntax for extracting parts of a collection\n# syntax: collection[startIndex:stopIndex(:step)]\n# startIndex defaults to 0; stopIndex defaults to len(col); step defaults to 1, negative step reverses direction of iteration\nprint(names[1:2])  # paul\nprint(names[1:])  # all except the first: paul, florian, albert\nprint(names[:1])  # get first element: peter\nprint(names[:])  # copy whole collection\nprint(names[::-1])  # reverse list: ['albert', 'florian', 'paul', 'peter']\n# works also for strings\nprint(\"hello nice world\"[6:10])  # nice\n"
  },
  {
    "path": "python-demo/3functions.py",
    "content": "def copy(source_file, target_file, override):\n    pass  # imagine some code here...\n\n# unclear what the parameters mean:\ncopy(\"~/file.txt\", \"~/file2.txt\", True)\n# named parameters make method calls readable!\ncopy(source_file=\"~/file.txt\", target_file=\"~/file2.txt\", override=True)\n# it's also more secure, because an error is thrown, if the argument name doesn't exist\n\n# default parameters! no silly constructor chaining.\ndef copy2(source_file, target_file, override = True):\n    pass  # imagine some code here...\ncopy2(source_file=\"~/file.txt\", target_file=\"~/file2.txt\")\n\n# functions are first class citizens. You can assign them to variables, return and pass them around\nmy_copy = copy2\n\n# lambdas\nget_year = lambda date: date.year\nimport datetime\ntoday = datetime.date.today()\nsome_day = datetime.date(2010, 9, 15)\nfor date in [today, some_day]:\n    print(get_year(date))  # 2016, 2010\n\n\n# multiple return values via automatic tuple packing and unpacking\ndef multiple_return():\n    return 1, 2, True  # will be packed to a tuple\nprint(multiple_return())  # (1, 2, True)\n\nfirst, second, third = multiple_return()  # unpacking\nprint(first, second, third)  # 1 2 True\n"
  },
  {
    "path": "python-demo/4classes.py",
    "content": "# let's define a User class with the properties name and age\nclass User:\n    def __init__(self, name, age):\n        self.name = name\n        self.age = age\n\n    def introduce_yourself(self):\n        print(\"Hi, I'm {}, {} years old\".format(self.name, self.age))\n\n# create a User\nuser = User(\"Hauke\", 28)\nprint(user.name)  # Hauke\nuser.introduce_yourself()  # Hi, I'm Hauke, 28 years old\n\n# by the way there is also Inheritance and Polymorphism\n"
  },
  {
    "path": "python-demo/5operator-overloading.py",
    "content": "import datetime\ntoday = datetime.date.today()\nsome_day = datetime.date(2016, 9, 15)\nprint(today - some_day)  # \"17 days, 0:00:00\". difference between dates.\n\nset1 = {1, 2, 3}\nset2 = {3, 4, 5}\nprint(set2 - set1)  # {4, 5}. difference between sets\n\n# Let's define our own overloading\n# Therefore, we use Protocols. Protocols define method signatures. If you implement these methods, you can use certain operators.\n# e.g. for \"==\" we need to implement \"__eq__\"\nclass User:\n    def __init__(self, name, age):\n        self.name = name\n        self.age = age\n\n    def __eq__(self, other):\n        return self.name is other.name and self.age is other.age\n\nprint(User(\"Hauke\", 28) == User(\"Peter\", 30))  # False\n\n# e.g. for \"list[element]\" we need to implement \"__getitem__\"\nclass UserAgeFinder:\n    def __init__(self):\n        self.user_list = [User(\"Hauke\", 28), User(\"Peter\", 30), User(\"Hugo\", 55)]\n\n    def __getitem__(self, name):\n        return [user.age for user in self.user_list if user.name is name]\n\nfinder = UserAgeFinder()\nprint(finder[\"Hauke\"])  # [28]\n\n"
  },
  {
    "path": "rest-api-doc-jaxrs-swagger-asciidoc/.gitignore",
    "content": ".idea/\n*.iml\ndependency-reduced-pom.xml\ntarget/"
  },
  {
    "path": "rest-api-doc-jaxrs-swagger-asciidoc/README.md",
    "content": "# rest-api-doc-jaxrs-swagger-asciidoc\n\n## Build and Start\n\n```bash\nmvn package\njava -jar target/rest-api-doc-jaxrs-swagger-asciidoc-1.0-SNAPSHOT.jar server config.yml\n```\n\n## Resources\n \n```\n# API:\nGET http://localhost:8080/bands\nGET http://localhost:8080/bands/<uuid>\nPOST http://localhost:8080/bands\n\n# Swagger Specification:\nGET http://localhost:8080/swagger.json\nGET http://localhost:8080/swagger.yaml\n\n# API HTML Documentation:\nGET http://localhost:8080/application-doc.html\n```\n\n## Optional: Swagger-UI\n\n```bash\n# start application\ncd swagger-ui\ndocker-compose up\n# open in browser: http://localhost:8090/?url=http://localhost:8080/swagger.json\n```"
  },
  {
    "path": "rest-api-doc-jaxrs-swagger-asciidoc/config.yml",
    "content": "logging:\n  level: INFO\n  loggers:\n    de.philipphauer.blog: DEBUG\n"
  },
  {
    "path": "rest-api-doc-jaxrs-swagger-asciidoc/generate-documentation.sh",
    "content": "#!/usr/bin/env bash\n# Convenience script for development to generate only the HTML Documentation without executing the whole Maven lifecycle.\n\nset -e\n\ncase \"$1\" in\n    '')\n        cat <<HEREDOC\nGenerating everything (swagger spec, asciidoc, html).\nPlease mind to force recompilation of your resource classes upfront (Ctrl+Shift+F9 in IDEA).\nHEREDOC\n        mvn exec:java@generate-swagger-and-asciidoc asciidoctor:process-asciidoc@output-html\n       ;;\n    'only-html')\n        cat <<HEREDOC\nOnly generating html.\nAssuming that only the asciidoc has changed.\nHEREDOC\n        mvn asciidoctor:process-asciidoc@output-html\n        ;;\n    *)\n        cat <<HEREDOC\nUnknown argument: $1\nUsage: $0 [only-html]\nHEREDOC\n        exit 1\n        ;;\nesac\n\necho \"Result: file://$PWD/target/classes/index.html\"\n"
  },
  {
    "path": "rest-api-doc-jaxrs-swagger-asciidoc/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project\n        xmlns=\"http://maven.apache.org/POM/4.0.0\"\n        xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n        xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <prerequisites>\n        <maven>3.0.0</maven>\n    </prerequisites>\n\n    <groupId>de.philipphauer.blog</groupId>\n    <artifactId>rest-api-doc-jaxrs-swagger-asciidoc</artifactId>\n    <version>1.0-SNAPSHOT</version>\n    <packaging>jar</packaging>\n    <name>rest-api-doc-jaxrs-swagger-asciidoc</name>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <dropwizard.version>1.0.6</dropwizard.version>\n        <mainClass>de.philipphauer.blog.RestApiDocApplication</mainClass>\n\n        <asciidoctor.input.directory>${project.basedir}/src/docs/asciidoc</asciidoctor.input.directory>\n        <generated.asciidoc.directory>${project.build.directory}/asciidoc/generated</generated.asciidoc.directory>\n        <asciidoctor.html.output.directory>${project.build.outputDirectory}</asciidoctor.html.output.directory><!-- target/classes -->\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>io.dropwizard</groupId>\n                <artifactId>dropwizard-bom</artifactId>\n                <version>${dropwizard.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>io.dropwizard</groupId>\n            <artifactId>dropwizard-core</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>io.swagger</groupId>\n            <artifactId>swagger-jersey2-jaxrs</artifactId>\n            <version>1.5.10</version>\n        </dependency>\n        <!-- don't forget to declare the jcenter repository (see below) -->\n        <dependency>\n            <groupId>io.github.swagger2markup</groupId>\n            <artifactId>swagger2markup</artifactId>\n            <version>1.2.0</version>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.codehaus.mojo</groupId>\n                <artifactId>exec-maven-plugin</artifactId>\n                <version>1.5.0</version>\n                <configuration>\n                    <mainClass>de.philipphauer.blog.apiDocGen.SwaggerAndAsciiDocGenerator</mainClass>\n                    <arguments>\n                        <argument>${project.build.outputDirectory}</argument> <!-- target/classes -->\n                        <argument>${generated.asciidoc.directory}</argument>\n                    </arguments>\n                </configuration>\n                <executions>\n                    <execution>\n                        <id>generate-swagger-and-asciidoc</id>\n                        <phase>process-classes</phase>\n                        <goals>\n                            <goal>java</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <groupId>org.asciidoctor</groupId>\n                <artifactId>asciidoctor-maven-plugin</artifactId>\n                <version>1.5.3</version>\n                <configuration>\n                    <sourceDirectory>${asciidoctor.input.directory}</sourceDirectory>\n                    <sourceDocumentName>index.adoc</sourceDocumentName>\n                    <attributes>\n                        <icons>font</icons>\n                        <doctype>book</doctype>\n                        <toc>left</toc>\n                        <toclevels>3</toclevels>\n                        <numbered></numbered>\n                        <hardbreaks></hardbreaks>\n                        <sectlinks></sectlinks>\n                        <sectanchors></sectanchors>\n                        <generated>${generated.asciidoc.directory}</generated>\n                        <stylesheet>custom.css</stylesheet>\n                    </attributes>\n                </configuration>\n                <executions>\n                    <execution>\n                        <id>output-html</id>\n                        <phase>test</phase>\n                        <goals>\n                            <goal>process-asciidoc</goal>\n                        </goals>\n                        <configuration>\n                            <backend>html5</backend>\n                            <sourceHighlighter>highlightjs</sourceHighlighter>\n                            <outputDirectory>${asciidoctor.html.output.directory}</outputDirectory>\n                        </configuration>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <artifactId>maven-shade-plugin</artifactId>\n                <version>2.4.1</version>\n                <configuration>\n                    <createDependencyReducedPom>true</createDependencyReducedPom>\n                    <transformers>\n                        <transformer implementation=\"org.apache.maven.plugins.shade.resource.ServicesResourceTransformer\"/>\n                        <transformer implementation=\"org.apache.maven.plugins.shade.resource.ManifestResourceTransformer\">\n                            <mainClass>${mainClass}</mainClass>\n                        </transformer>\n                    </transformers>\n                    <!-- exclude signed Manifests -->\n                    <filters>\n                        <filter>\n                            <artifact>*:*</artifact>\n                            <excludes>\n                                <exclude>META-INF/*.SF</exclude>\n                                <exclude>META-INF/*.DSA</exclude>\n                                <exclude>META-INF/*.RSA</exclude>\n                            </excludes>\n                        </filter>\n                    </filters>\n                </configuration>\n                <executions>\n                    <execution>\n                        <phase>package</phase>\n                        <goals>\n                            <goal>shade</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <artifactId>maven-jar-plugin</artifactId>\n                <version>2.6</version>\n                <configuration>\n                    <archive>\n                        <manifest>\n                            <addClasspath>true</addClasspath>\n                            <mainClass>${mainClass}</mainClass>\n                        </manifest>\n                    </archive>\n                </configuration>\n            </plugin>\n            <plugin>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <version>3.3</version>\n                <configuration>\n                    <source>1.8</source>\n                    <target>1.8</target>\n                </configuration>\n            </plugin>\n            <plugin>\n                <artifactId>maven-source-plugin</artifactId>\n                <version>2.4</version>\n                <executions>\n                    <execution>\n                        <id>attach-sources</id>\n                        <goals>\n                            <goal>jar</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <artifactId>maven-javadoc-plugin</artifactId>\n                <version>2.10.3</version>\n                <executions>\n                    <execution>\n                        <id>attach-javadocs</id>\n                        <goals>\n                            <goal>jar</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n    <repositories>\n        <repository>\n            <snapshots>\n                <enabled>false</enabled>\n            </snapshots>\n            <id>jcenter-releases</id>\n            <name>jcenter</name>\n            <url>http://jcenter.bintray.com</url>\n        </repository>\n    </repositories>\n</project>\n"
  },
  {
    "path": "rest-api-doc-jaxrs-swagger-asciidoc/src/docs/asciidoc/custom.css",
    "content": "/* see below for custom styling */\n\n/* Asciidoctor default stylesheet | MIT License | http://asciidoctor.org */\n@import \"https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700\";\narticle,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}\naudio,canvas,video{display:inline-block}\naudio:not([controls]){display:none;height:0}\n[hidden],template{display:none}\nscript{display:none!important}\nhtml{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}\nbody{margin:0}\na{background:transparent}\na:focus{outline:thin dotted}\na:active,a:hover{outline:0}\nh1{font-size:2em;margin:.67em 0}\nabbr[title]{border-bottom:1px dotted}\nb,strong{font-weight:bold}\ndfn{font-style:italic}\nhr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}\nmark{background:#ff0;color:#000}\ncode,kbd,pre,samp{font-family:monospace;font-size:1em}\npre{white-space:pre-wrap}\nq{quotes:\"\\201C\" \"\\201D\" \"\\2018\" \"\\2019\"}\nsmall{font-size:80%}\nsub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}\nsup{top:-.5em}\nsub{bottom:-.25em}\nimg{border:0}\nsvg:not(:root){overflow:hidden}\nfigure{margin:0}\nfieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}\nlegend{border:0;padding:0}\nbutton,input,select,textarea{font-family:inherit;font-size:100%;margin:0}\nbutton,input{line-height:normal}\nbutton,select{text-transform:none}\nbutton,html input[type=\"button\"],input[type=\"reset\"],input[type=\"submit\"]{-webkit-appearance:button;cursor:pointer}\nbutton[disabled],html input[disabled]{cursor:default}\ninput[type=\"checkbox\"],input[type=\"radio\"]{box-sizing:border-box;padding:0}\ninput[type=\"search\"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}\ninput[type=\"search\"]::-webkit-search-cancel-button,input[type=\"search\"]::-webkit-search-decoration{-webkit-appearance:none}\nbutton::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}\ntextarea{overflow:auto;vertical-align:top}\ntable{border-collapse:collapse;border-spacing:0}\n*,*:before,*:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}\nhtml,body{font-size:100%}\nbody{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:\"Noto Serif\",\"DejaVu Serif\",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto}\na:hover{cursor:pointer}\nimg,object,embed{max-width:100%;height:auto}\nobject,embed{height:100%}\nimg{-ms-interpolation-mode:bicubic}\n.left{float:left!important}\n.right{float:right!important}\n.text-left{text-align:left!important}\n.text-right{text-align:right!important}\n.text-center{text-align:center!important}\n.text-justify{text-align:justify!important}\n.hide{display:none}\nbody{-webkit-font-smoothing:antialiased}\nimg,object,svg{display:inline-block;vertical-align:middle}\ntextarea{height:auto;min-height:50px}\nselect{width:100%}\n.center{margin-left:auto;margin-right:auto}\n.spread{width:100%}\np.lead,.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{font-size:1.21875em;line-height:1.6}\n.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}\ndiv,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0;direction:ltr}\na{color:#2156a5;text-decoration:underline;line-height:inherit}\na:hover,a:focus{color:#1d4b8f}\na img{border:none}\np{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}\np aside{font-size:.875em;line-height:1.35;font-style:italic}\nh1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:\"Open Sans\",\"DejaVu Sans\",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}\nh1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}\nh1{font-size:2.125em}\nh2{font-size:1.6875em}\nh3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}\nh4,h5{font-size:1.125em}\nh6{font-size:1em}\nhr{border:solid #ddddd8;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0}\nem,i{font-style:italic;line-height:inherit}\nstrong,b{font-weight:bold;line-height:inherit}\nsmall{font-size:60%;line-height:inherit}\ncode{font-family:\"Droid Sans Mono\",\"DejaVu Sans Mono\",monospace;font-weight:400;color:rgba(0,0,0,.9)}\nul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}\nul,ol,ul.no-bullet,ol.no-bullet{margin-left:1.5em}\nul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em}\nul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}\nul.square{list-style-type:square}\nul.circle{list-style-type:circle}\nul.disc{list-style-type:disc}\nul.no-bullet{list-style:none}\nol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}\ndl dt{margin-bottom:.3125em;font-weight:bold}\ndl dd{margin-bottom:1.25em}\nabbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help}\nabbr{text-transform:none}\nblockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}\nblockquote cite{display:block;font-size:.9375em;color:rgba(0,0,0,.6)}\nblockquote cite:before{content:\"\\2014 \\0020\"}\nblockquote cite a,blockquote cite a:visited{color:rgba(0,0,0,.6)}\nblockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}\n@media only screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}\n    h1{font-size:2.75em}\n    h2{font-size:2.3125em}\n    h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}\n    h4{font-size:1.4375em}}\ntable{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede}\ntable thead,table tfoot{background:#f7f8f7;font-weight:bold}\ntable thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}\ntable tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}\ntable tr.even,table tr.alt,table tr:nth-of-type(even){background:#f8f8f7}\ntable thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{display:table-cell;line-height:1.6}\nbody{tab-size:4}\nh1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}\nh1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}\n.clearfix:before,.clearfix:after,.float-group:before,.float-group:after{content:\" \";display:table}\n.clearfix:after,.float-group:after{clear:both}\n*:not(pre)>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background-color:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed}\npre,pre>code{line-height:1.45;color:rgba(0,0,0,.9);font-family:\"Droid Sans Mono\",\"DejaVu Sans Mono\",monospace;font-weight:400;text-rendering:optimizeSpeed}\n.keyseq{color:rgba(51,51,51,.8)}\nkbd{font-family:\"Droid Sans Mono\",\"DejaVu Sans Mono\",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background-color:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}\n.keyseq kbd:first-child{margin-left:0}\n.keyseq kbd:last-child{margin-right:0}\n.menuseq,.menu{color:rgba(0,0,0,.8)}\nb.button:before,b.button:after{position:relative;top:-1px;font-weight:400}\nb.button:before{content:\"[\";padding:0 3px 0 2px}\nb.button:after{content:\"]\";padding:0 2px 0 3px}\np a>code:hover{color:rgba(0,0,0,.9)}\n#header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}\n#header:before,#header:after,#content:before,#content:after,#footnotes:before,#footnotes:after,#footer:before,#footer:after{content:\" \";display:table}\n#header:after,#content:after,#footnotes:after,#footer:after{clear:both}\n#content{margin-top:1.25em}\n#content:before{content:none}\n#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}\n#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #ddddd8}\n#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #ddddd8;padding-bottom:8px}\n#header .details{border-bottom:1px solid #ddddd8;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap}\n#header .details span:first-child{margin-left:-.125em}\n#header .details span.email a{color:rgba(0,0,0,.85)}\n#header .details br{display:none}\n#header .details br+span:before{content:\"\\00a0\\2013\\00a0\"}\n#header .details br+span.author:before{content:\"\\00a0\\22c5\\00a0\";color:rgba(0,0,0,.85)}\n#header .details br+span#revremark:before{content:\"\\00a0|\\00a0\"}\n#header #revnumber{text-transform:capitalize}\n#header #revnumber:after{content:\"\\00a0\"}\n#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #ddddd8;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}\n#toc{border-bottom:1px solid #efefed;padding-bottom:.5em}\n#toc>ul{margin-left:.125em}\n#toc ul.sectlevel0>li>a{font-style:italic}\n#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}\n#toc ul{font-family:\"Open Sans\",\"DejaVu Sans\",sans-serif;list-style-type:none}\n#toc li{line-height:1.3334;margin-top:.3334em}\n#toc a{text-decoration:none}\n#toc a:active{text-decoration:underline}\n#toctitle{color:#7a2518;font-size:1.2em}\n@media only screen and (min-width:768px){#toctitle{font-size:1.375em}\n    body.toc2{padding-left:15em;padding-right:0}\n    #toc.toc2{margin-top:0!important;background-color:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #efefed;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}\n    #toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}\n    #toc.toc2>ul{font-size:.9em;margin-bottom:0}\n    #toc.toc2 ul ul{margin-left:0;padding-left:1em}\n    #toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}\n    body.toc2.toc-right{padding-left:0;padding-right:15em}\n    body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #efefed;left:auto;right:0}}\n@media only screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}\n    #toc.toc2{width:20em}\n    #toc.toc2 #toctitle{font-size:1.375em}\n    #toc.toc2>ul{font-size:.95em}\n    #toc.toc2 ul ul{padding-left:1.25em}\n    body.toc2.toc-right{padding-left:0;padding-right:20em}}\n#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}\n#content #toc>:first-child{margin-top:0}\n#content #toc>:last-child{margin-bottom:0}\n#footer{max-width:100%;background-color:rgba(0,0,0,.8);padding:1.25em}\n#footer-text{color:rgba(255,255,255,.8);line-height:1.44}\n.sect1{padding-bottom:.625em}\n@media only screen and (min-width:768px){.sect1{padding-bottom:1.25em}}\n.sect1+.sect1{border-top:1px solid #efefed}\n#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}\n#content h1>a.anchor:before,h2>a.anchor:before,h3>a.anchor:before,#toctitle>a.anchor:before,.sidebarblock>.content>.title>a.anchor:before,h4>a.anchor:before,h5>a.anchor:before,h6>a.anchor:before{content:\"\\00A7\";font-size:.85em;display:block;padding-top:.1em}\n#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}\n#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}\n#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}\n.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}\n.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:\"Noto Serif\",\"DejaVu Serif\",serif;font-size:1rem;font-style:italic}\ntable.tableblock>caption.title{white-space:nowrap;overflow:visible;max-width:0}\n.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{color:rgba(0,0,0,.85)}\ntable.tableblock #preamble>.sectionbody>.paragraph:first-of-type p{font-size:inherit}\n.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%}\n.admonitionblock>table td.icon{text-align:center;width:80px}\n.admonitionblock>table td.icon img{max-width:none}\n.admonitionblock>table td.icon .title{font-weight:bold;font-family:\"Open Sans\",\"DejaVu Sans\",sans-serif;text-transform:uppercase}\n.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #ddddd8;color:rgba(0,0,0,.6)}\n.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}\n.exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px}\n.exampleblock>.content>:first-child{margin-top:0}\n.exampleblock>.content>:last-child{margin-bottom:0}\n.sidebarblock{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}\n.sidebarblock>:first-child{margin-top:0}\n.sidebarblock>:last-child{margin-bottom:0}\n.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}\n.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}\n.literalblock pre,.listingblock pre:not(.highlight),.listingblock pre[class=\"highlight\"],.listingblock pre[class^=\"highlight \"],.listingblock pre.CodeRay,.listingblock pre.prettyprint{background:#f7f7f8}\n.sidebarblock .literalblock pre,.sidebarblock .listingblock pre:not(.highlight),.sidebarblock .listingblock pre[class=\"highlight\"],.sidebarblock .listingblock pre[class^=\"highlight \"],.sidebarblock .listingblock pre.CodeRay,.sidebarblock .listingblock pre.prettyprint{background:#f2f1f1}\n.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{-webkit-border-radius:4px;border-radius:4px;word-wrap:break-word;padding:1em;font-size:.8125em}\n.literalblock pre.nowrap,.literalblock pre[class].nowrap,.listingblock pre.nowrap,.listingblock pre[class].nowrap{overflow-x:auto;white-space:pre;word-wrap:normal}\n@media only screen and (min-width:768px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:.90625em}}\n@media only screen and (min-width:1280px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:1em}}\n.literalblock.output pre{color:#f7f7f8;background-color:rgba(0,0,0,.9)}\n.listingblock pre.highlightjs{padding:0}\n.listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px}\n.listingblock pre.prettyprint{border-width:0}\n.listingblock>.content{position:relative}\n.listingblock code[data-lang]:before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:#999}\n.listingblock:hover code[data-lang]:before{display:block}\n.listingblock.terminal pre .command:before{content:attr(data-prompt);padding-right:.5em;color:#999}\n.listingblock.terminal pre .command:not([data-prompt]):before{content:\"$\"}\ntable.pyhltable{border-collapse:separate;border:0;margin-bottom:0;background:none}\ntable.pyhltable td{vertical-align:top;padding-top:0;padding-bottom:0;line-height:1.45}\ntable.pyhltable td.code{padding-left:.75em;padding-right:0}\npre.pygments .lineno,table.pyhltable td:not(.code){color:#999;padding-left:0;padding-right:.5em;border-right:1px solid #ddddd8}\npre.pygments .lineno{display:inline-block;margin-right:.25em}\ntable.pyhltable .linenodiv{background:none!important;padding-right:0!important}\n.quoteblock{margin:0 1em 1.25em 1.5em;display:table}\n.quoteblock>.title{margin-left:-1.5em;margin-bottom:.75em}\n.quoteblock blockquote,.quoteblock blockquote p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}\n.quoteblock blockquote{margin:0;padding:0;border:0}\n.quoteblock blockquote:before{content:\"\\201c\";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}\n.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}\n.quoteblock .attribution{margin-top:.5em;margin-right:.5ex;text-align:right}\n.quoteblock .quoteblock{margin-left:0;margin-right:0;padding:.5em 0;border-left:3px solid rgba(0,0,0,.6)}\n.quoteblock .quoteblock blockquote{padding:0 0 0 .75em}\n.quoteblock .quoteblock blockquote:before{display:none}\n.verseblock{margin:0 1em 1.25em 1em}\n.verseblock pre{font-family:\"Open Sans\",\"DejaVu Sans\",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}\n.verseblock pre strong{font-weight:400}\n.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}\n.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}\n.quoteblock .attribution br,.verseblock .attribution br{display:none}\n.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}\n.quoteblock.abstract{margin:0 0 1.25em 0;display:block}\n.quoteblock.abstract blockquote,.quoteblock.abstract blockquote p{text-align:left;word-spacing:0}\n.quoteblock.abstract blockquote:before,.quoteblock.abstract blockquote p:first-of-type:before{display:none}\ntable.tableblock{max-width:100%;border-collapse:separate}\ntable.tableblock td>.paragraph:last-child p>p:last-child,table.tableblock th>p:last-child,table.tableblock td>p:last-child{margin-bottom:0}\ntable.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}\ntable.grid-all th.tableblock,table.grid-all td.tableblock{border-width:0 1px 1px 0}\ntable.grid-all tfoot>tr>th.tableblock,table.grid-all tfoot>tr>td.tableblock{border-width:1px 1px 0 0}\ntable.grid-cols th.tableblock,table.grid-cols td.tableblock{border-width:0 1px 0 0}\ntable.grid-all *>tr>.tableblock:last-child,table.grid-cols *>tr>.tableblock:last-child{border-right-width:0}\ntable.grid-rows th.tableblock,table.grid-rows td.tableblock{border-width:0 0 1px 0}\ntable.grid-all tbody>tr:last-child>th.tableblock,table.grid-all tbody>tr:last-child>td.tableblock,table.grid-all thead:last-child>tr>th.tableblock,table.grid-rows tbody>tr:last-child>th.tableblock,table.grid-rows tbody>tr:last-child>td.tableblock,table.grid-rows thead:last-child>tr>th.tableblock{border-bottom-width:0}\ntable.grid-rows tfoot>tr>th.tableblock,table.grid-rows tfoot>tr>td.tableblock{border-width:1px 0 0 0}\ntable.frame-all{border-width:1px}\ntable.frame-sides{border-width:0 1px}\ntable.frame-topbot{border-width:1px 0}\nth.halign-left,td.halign-left{text-align:left}\nth.halign-right,td.halign-right{text-align:right}\nth.halign-center,td.halign-center{text-align:center}\nth.valign-top,td.valign-top{vertical-align:top}\nth.valign-bottom,td.valign-bottom{vertical-align:bottom}\nth.valign-middle,td.valign-middle{vertical-align:middle}\ntable thead th,table tfoot th{font-weight:bold}\ntbody tr th{display:table-cell;line-height:1.6;background:#f7f8f7}\ntbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}\np.tableblock>code:only-child{background:none;padding:0}\np.tableblock{font-size:1em}\ntd>div.verse{white-space:pre}\nol{margin-left:1.75em}\nul li ol{margin-left:1.5em}\ndl dd{margin-left:1.125em}\ndl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}\nol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}\nul.unstyled,ol.unnumbered,ul.checklist,ul.none{list-style-type:none}\nul.unstyled,ol.unnumbered,ul.checklist{margin-left:.625em}\nul.checklist li>p:first-child>.fa-square-o:first-child,ul.checklist li>p:first-child>.fa-check-square-o:first-child{width:1em;font-size:.85em}\nul.checklist li>p:first-child>input[type=\"checkbox\"]:first-child{width:1em;position:relative;top:1px}\nul.inline{margin:0 auto .625em auto;margin-left:-1.375em;margin-right:0;padding:0;list-style:none;overflow:hidden}\nul.inline>li{list-style:none;float:left;margin-left:1.375em;display:block}\nul.inline>li>*{display:block}\n.unstyled dl dt{font-weight:400;font-style:normal}\nol.arabic{list-style-type:decimal}\nol.decimal{list-style-type:decimal-leading-zero}\nol.loweralpha{list-style-type:lower-alpha}\nol.upperalpha{list-style-type:upper-alpha}\nol.lowerroman{list-style-type:lower-roman}\nol.upperroman{list-style-type:upper-roman}\nol.lowergreek{list-style-type:lower-greek}\n.hdlist>table,.colist>table{border:0;background:none}\n.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none}\ntd.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}\ntd.hdlist1{font-weight:bold;padding-bottom:1.25em}\n.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}\n.colist>table tr>td:first-of-type{padding:0 .75em;line-height:1}\n.colist>table tr>td:last-of-type{padding:.25em 0}\n.thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd}\n.imageblock.left,.imageblock[style*=\"float: left\"]{margin:.25em .625em 1.25em 0}\n.imageblock.right,.imageblock[style*=\"float: right\"]{margin:.25em 0 1.25em .625em}\n.imageblock>.title{margin-bottom:0}\n.imageblock.thumb,.imageblock.th{border-width:6px}\n.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}\n.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}\n.image.left{margin-right:.625em}\n.image.right{margin-left:.625em}\na.image{text-decoration:none;display:inline-block}\na.image object{pointer-events:none}\nsup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}\nsup.footnote a,sup.footnoteref a{text-decoration:none}\nsup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}\n#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}\n#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em 0;border-width:1px 0 0 0}\n#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;text-indent:-1.05em;margin-bottom:.2em}\n#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none}\n#footnotes .footnote:last-of-type{margin-bottom:0}\n#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}\n.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0}\n.gist .file-data>table td.line-data{width:99%}\ndiv.unbreakable{page-break-inside:avoid}\n.big{font-size:larger}\n.small{font-size:smaller}\n.underline{text-decoration:underline}\n.overline{text-decoration:overline}\n.line-through{text-decoration:line-through}\n.aqua{color:#00bfbf}\n.aqua-background{background-color:#00fafa}\n.black{color:#000}\n.black-background{background-color:#000}\n.blue{color:#0000bf}\n.blue-background{background-color:#0000fa}\n.fuchsia{color:#bf00bf}\n.fuchsia-background{background-color:#fa00fa}\n.gray{color:#606060}\n.gray-background{background-color:#7d7d7d}\n.green{color:#006000}\n.green-background{background-color:#007d00}\n.lime{color:#00bf00}\n.lime-background{background-color:#00fa00}\n.maroon{color:#600000}\n.maroon-background{background-color:#7d0000}\n.navy{color:#000060}\n.navy-background{background-color:#00007d}\n.olive{color:#606000}\n.olive-background{background-color:#7d7d00}\n.purple{color:#600060}\n.purple-background{background-color:#7d007d}\n.red{color:#bf0000}\n.red-background{background-color:#fa0000}\n.silver{color:#909090}\n.silver-background{background-color:#bcbcbc}\n.teal{color:#006060}\n.teal-background{background-color:#007d7d}\n.white{color:#bfbfbf}\n.white-background{background-color:#fafafa}\n.yellow{color:#bfbf00}\n.yellow-background{background-color:#fafa00}\nspan.icon>.fa{cursor:default}\n.admonitionblock td.icon [class^=\"fa icon-\"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}\n.admonitionblock td.icon .icon-note:before{content:\"\\f05a\";color:#19407c}\n.admonitionblock td.icon .icon-tip:before{content:\"\\f0eb\";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}\n.admonitionblock td.icon .icon-warning:before{content:\"\\f071\";color:#bf6900}\n.admonitionblock td.icon .icon-caution:before{content:\"\\f06d\";color:#bf3400}\n.admonitionblock td.icon .icon-important:before{content:\"\\f06a\";color:#bf0000}\n.conum[data-value]{display:inline-block;color:#fff!important;background-color:rgba(0,0,0,.8);-webkit-border-radius:100px;border-radius:100px;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:\"Open Sans\",\"DejaVu Sans\",sans-serif;font-style:normal;font-weight:bold}\n.conum[data-value] *{color:#fff!important}\n.conum[data-value]+b{display:none}\n.conum[data-value]:after{content:attr(data-value)}\npre .conum[data-value]{position:relative;top:-.125em}\nb.conum *{color:inherit!important}\n.conum:not([data-value]):empty{display:none}\ndt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}\nh1,h2,p,td.content,span.alt{letter-spacing:-.01em}\np strong,td.content strong,div.footnote strong{letter-spacing:-.005em}\np,blockquote,dt,td.content,span.alt{font-size:1.0625rem}\np{margin-bottom:1.25rem}\n.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}\n.exampleblock>.content{background-color:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc}\n.print-only{display:none!important}\n@media print{@page{margin:1.25cm .75cm}\n    *{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important}\n    a{color:inherit!important;text-decoration:underline!important}\n    a.bare,a[href^=\"#\"],a[href^=\"mailto:\"]{text-decoration:none!important}\n    a[href^=\"http:\"]:not(.bare):after,a[href^=\"https:\"]:not(.bare):after{content:\"(\" attr(href) \")\";display:inline-block;font-size:.875em;padding-left:.25em}\n    abbr[title]:after{content:\" (\" attr(title) \")\"}\n    pre,blockquote,tr,img,object,svg{page-break-inside:avoid}\n    thead{display:table-header-group}\n    svg{max-width:100%}\n    p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}\n    h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}\n    #toc,.sidebarblock,.exampleblock>.content{background:none!important}\n    #toc{border-bottom:1px solid #ddddd8!important;padding-bottom:0!important}\n    .sect1{padding-bottom:0!important}\n    .sect1+.sect1{border:0!important}\n    #header>h1:first-child{margin-top:1.25rem}\n    body.book #header{text-align:center}\n    body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em 0}\n    body.book #header .details{border:0!important;display:block;padding:0!important}\n    body.book #header .details span:first-child{margin-left:0!important}\n    body.book #header .details br{display:block}\n    body.book #header .details br+span:before{content:none!important}\n    body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}\n    body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}\n    .listingblock code[data-lang]:before{display:block}\n    #footer{background:none!important;padding:0 .9375em}\n    #footer-text{color:rgba(0,0,0,.6)!important;font-size:.9em}\n    .hide-on-print{display:none!important}\n    .print-only{display:block!important}\n    .hide-for-print{display:none!important}\n    .show-for-print{display:inherit!important}}\n\n/* custom styling */\nh1,h2,h3,h4,h5,div#toctitle{\n    color: #428bca;\n}\ndiv#header{\n    background: url(\"https://blog.philipphauer.de/img/philipphauer-logo.png\") no-repeat;\n    background-size: 15px;\n    background-position-x: 15px;\n    background-position-y: 35px;\n    padding-left: 47px;\n    border-bottom: 1px solid #ddddd8;\n}\ndiv#header h1{\n    border-bottom: none !important;\n}\na {\n    color: #428bca;\n}\na:hover, a:active{\n    color: #295d83;\n}\ndiv.admonitionblock>table td.content {\n    color: rgba(0,0,0,.8);\n}"
  },
  {
    "path": "rest-api-doc-jaxrs-swagger-asciidoc/src/docs/asciidoc/general-remarks.adoc",
    "content": "\n== General Remarks\n\nPlaceholder for your manually written documenation...\n\nWARNING: AsciiDoc Warning!"
  },
  {
    "path": "rest-api-doc-jaxrs-swagger-asciidoc/src/docs/asciidoc/index.adoc",
    "content": "include::{generated}/overview.adoc[]\ninclude::general-remarks.adoc[]\ninclude::usage.adoc[]\ninclude::{generated}/paths.adoc[]\ninclude::{generated}/security.adoc[]\ninclude::{generated}/definitions.adoc[]"
  },
  {
    "path": "rest-api-doc-jaxrs-swagger-asciidoc/src/docs/asciidoc/usage.adoc",
    "content": "\n== Usage\n\nEven more manually written documentation. For instance, you can describe common use cases or workflows for your API here.\n\n=== Retrieve all Bands\n\nJust call `GET` on the `link:#_getbands[/bands]` resource. It will return link:#_payload_for_band_retrieval[band objects].\n\n[source,json]\n----\n[\n    {\n        \"foundation\": 1997,\n        \"id\": \"faf86775-ca8e-414b-bfe3-fbe2cd378a05\",\n        \"name\": \"Flogging Molly\"\n    }\n]\n----\n\nAs you can see, you can also link to the generated documentation."
  },
  {
    "path": "rest-api-doc-jaxrs-swagger-asciidoc/src/main/java/de/philipphauer/blog/RestApiDocApplication.java",
    "content": "package de.philipphauer.blog;\n\nimport de.philipphauer.blog.resources.BandResource;\nimport de.philipphauer.blog.resources.CORSFilter;\nimport de.philipphauer.blog.resources.DocumentationResource;\nimport io.dropwizard.Application;\nimport io.dropwizard.setup.Bootstrap;\nimport io.dropwizard.setup.Environment;\n\npublic class RestApiDocApplication extends Application<RestApiDocConfiguration> {\n\n    public static void main(final String[] args) throws Exception {\n        new RestApiDocApplication().run(args);\n    }\n\n    @Override\n    public String getName() {\n        return \"RestApiDocApplication\";\n    }\n\n    @Override\n    public void initialize(final Bootstrap<RestApiDocConfiguration> bootstrap) {\n    }\n\n    @Override\n    public void run(final RestApiDocConfiguration configuration,\n                    final Environment environment) {\n        environment.jersey().register(BandResource.class);\n        environment.jersey().register(DocumentationResource.class);\n        environment.jersey().register(CORSFilter.class);\n    }\n\n}\n"
  },
  {
    "path": "rest-api-doc-jaxrs-swagger-asciidoc/src/main/java/de/philipphauer/blog/RestApiDocConfiguration.java",
    "content": "package de.philipphauer.blog;\n\nimport io.dropwizard.Configuration;\n\npublic class RestApiDocConfiguration extends Configuration {\n}\n"
  },
  {
    "path": "rest-api-doc-jaxrs-swagger-asciidoc/src/main/java/de/philipphauer/blog/apiDocGen/SwaggerAndAsciiDocGenerator.java",
    "content": "package de.philipphauer.blog.apiDocGen;\n\nimport io.github.swagger2markup.Swagger2MarkupConverter;\nimport io.swagger.jaxrs.config.BeanConfig;\nimport io.swagger.models.Swagger;\nimport io.swagger.util.Json;\nimport io.swagger.util.Yaml;\n\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\n\n/**\n * a) parses the resource classes and generates the swagger spec,\n * b) takes the swagger spec and generates the asciidoc.\n * <br>\n * Arguments:\n * arg[0]: target folder for swagger.json and swagger.yaml. e.g. \"target/classes\"\n * arg[1]: target folder for the generated asciidoc. e.g. \"target/asciidoc/generated\"\n */\npublic class SwaggerAndAsciiDocGenerator {\n\n    public static void main(String[] args) throws IOException {\n        Path swaggerTargetFolder = Paths.get(args[0]);\n        Path asciiDocTargetFolder = Paths.get(args[1]);\n\n        System.out.println(\"Creating Swagger Spec in \" + swaggerTargetFolder);\n        createSwagger(swaggerTargetFolder);\n        System.out.println(\"Generating AsciiDoc in \" + asciiDocTargetFolder);\n        convertSwaggerToAsciiDoc(swaggerTargetFolder, asciiDocTargetFolder);\n        System.out.println(\"Done.\");\n    }\n\n    private static void createSwagger(Path swaggerTargetFolder) throws IOException {\n        BeanConfig config = new BeanConfig();\n        config.setVersion(\"v1\");\n        config.setTitle(\"Band API\");\n        config.setDescription(\"An API to retrieve and create bands.\");\n        config.setSchemes(new String[]{\"http\"});\n        config.setHost(\"localhost:8080\");\n        config.setBasePath(\"\");\n        config.setResourcePackage(\"de.philipphauer.blog.resources\");\n        config.setScan();//this \"setter\" triggers the scanning... nice naming...\n        Swagger swagger = config.getSwagger();\n\n        String json = Json.pretty().writeValueAsString(swagger);\n        Path jsonFile = swaggerTargetFolder.resolve(\"swagger.json\");\n        Files.write(jsonFile, json.getBytes(StandardCharsets.UTF_8));\n\n        String yaml = Yaml.mapper().writeValueAsString(swagger);\n        Path yamlFile = swaggerTargetFolder.resolve(\"swagger.yaml\");\n        Files.write(yamlFile, yaml.getBytes(StandardCharsets.UTF_8));\n    }\n\n    private static void convertSwaggerToAsciiDoc(Path swaggerTargetFolder, Path asciiDocTargetFolder){\n        Path swaggerFile = swaggerTargetFolder.resolve(\"swagger.json\");\n        Swagger2MarkupConverter.from(swaggerFile)\n                .build()\n                .toFolder(asciiDocTargetFolder);\n    }\n}\n"
  },
  {
    "path": "rest-api-doc-jaxrs-swagger-asciidoc/src/main/java/de/philipphauer/blog/resources/BandResource.java",
    "content": "package de.philipphauer.blog.resources;\n\nimport com.google.common.collect.ImmutableList;\nimport de.philipphauer.blog.resources.dto.BandCreationDTO;\nimport de.philipphauer.blog.resources.dto.BandRetrievalDTO;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport io.swagger.annotations.ApiParam;\nimport io.swagger.annotations.ApiResponse;\nimport io.swagger.annotations.ApiResponses;\nimport io.swagger.annotations.ResponseHeader;\n\nimport javax.ws.rs.Consumes;\nimport javax.ws.rs.DefaultValue;\nimport javax.ws.rs.GET;\nimport javax.ws.rs.POST;\nimport javax.ws.rs.Path;\nimport javax.ws.rs.PathParam;\nimport javax.ws.rs.Produces;\nimport javax.ws.rs.QueryParam;\nimport javax.ws.rs.core.MediaType;\nimport javax.ws.rs.core.Response;\nimport java.util.List;\nimport java.util.UUID;\n\n@Path(\"/\")\n@Produces(MediaType.APPLICATION_JSON)\n@Api\npublic class BandResource {\n\n    private static final BandRetrievalDTO A_BAND = new BandRetrievalDTO()\n            .setId(UUID.randomUUID())\n            .setName(\"Flogging Molly\")\n            .setFoundation(1997);\n\n    @GET\n    @Path(\"/bands\")\n    @ApiOperation(value = \"Retrieve all Bands\", notes = \"See [usage](#_retrieve_all_bands).\")\n    public List<BandRetrievalDTO> getBands(@QueryParam(\"offset\") @DefaultValue(\"0\") int offset,\n                                           @QueryParam(\"limit\") @DefaultValue(\"100\") int limit) {\n        return ImmutableList.of(A_BAND);\n    }\n\n    @GET\n    @Path(\"/bands/{bandId}\")\n    @ApiOperation(value = \"Retrieve a single Band\", notes = \"Description\")\n    public BandRetrievalDTO getBand(@PathParam(\"bandId\") String bandId) {\n        return A_BAND;\n    }\n\n    @POST\n    @Path(\"/bands/\")\n    @Consumes(MediaType.APPLICATION_JSON)\n    @ApiOperation(value = \"Create a new Band\", notes = \"Description\")\n    @ApiResponses(value = @ApiResponse(code = 201, message = \"Successfully created band\",\n            responseHeaders = @ResponseHeader(name = \"Location\", description = \"URL of the created band. e.g. `/band/<uuid>`\")\n    ))\n    public Response createBand(@ApiParam(required = true) BandCreationDTO newBand) {\n        System.out.println(newBand);\n        return null;\n    }\n}"
  },
  {
    "path": "rest-api-doc-jaxrs-swagger-asciidoc/src/main/java/de/philipphauer/blog/resources/CORSFilter.java",
    "content": "package de.philipphauer.blog.resources;\n\nimport javax.ws.rs.container.ContainerRequestContext;\nimport javax.ws.rs.container.ContainerResponseContext;\nimport javax.ws.rs.container.ContainerResponseFilter;\nimport javax.ws.rs.ext.Provider;\nimport java.io.IOException;\n\n@Provider\npublic class CORSFilter implements ContainerResponseFilter {\n\n    @Override\n    public void filter(ContainerRequestContext requestContext,\n                       ContainerResponseContext responseContext) throws IOException {\n        responseContext.getHeaders().add(\"Access-Control-Allow-Origin\", \"*\");\n        responseContext.getHeaders().add(\"Access-Control-Allow-Methods\", \"GET, POST, PUT, DELETE, OPTIONS, HEAD\");\n        responseContext.getHeaders().add(\"Access-Control-Allow-Headers\", \"origin, content-type, accept, authorization\");\n        responseContext.getHeaders().add(\"Access-Control-Allow-Credentials\", \"true\");\n        responseContext.getHeaders().add(\"Access-Control-Max-Age\", \"1209600\");\n    }\n\n}"
  },
  {
    "path": "rest-api-doc-jaxrs-swagger-asciidoc/src/main/java/de/philipphauer/blog/resources/DocumentationResource.java",
    "content": "package de.philipphauer.blog.resources;\n\nimport com.google.common.base.Throwables;\nimport com.google.common.io.Resources;\n\nimport javax.ws.rs.GET;\nimport javax.ws.rs.Path;\nimport javax.ws.rs.Produces;\nimport javax.ws.rs.core.MediaType;\nimport java.io.IOException;\nimport java.net.URL;\nimport java.nio.charset.StandardCharsets;\n\n@Path(\"/\")\npublic class DocumentationResource {\n\n    @GET\n    @Path(\"swagger.json\")\n    @Produces(MediaType.APPLICATION_JSON)\n    public String swaggerJson() {\n        return getFileContent(\"swagger.json\");\n    }\n\n    @GET\n    @Path(\"swagger.yaml\")\n    @Produces(\"application/yaml\")\n    public String swaggerYaml(){\n        return getFileContent(\"swagger.yaml\");\n    }\n\n    @GET\n    @Path(\"application-doc.html\")\n    @Produces(MediaType.TEXT_HTML)\n    public String doc(){\n        return getFileContent(\"index.html\");\n    }\n\n    private String getFileContent(String fileName) {\n        try {\n            URL url = Resources.getResource(fileName);\n            return Resources.toString(url, StandardCharsets.UTF_8);\n        } catch (IOException e) {\n            throw Throwables.propagate(e);\n        }\n    }\n}\n"
  },
  {
    "path": "rest-api-doc-jaxrs-swagger-asciidoc/src/main/java/de/philipphauer/blog/resources/dto/BandCreationDTO.java",
    "content": "package de.philipphauer.blog.resources.dto;\n\nimport com.google.common.base.MoreObjects;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\n\n@ApiModel(\"Payload for band creation\")\npublic class BandCreationDTO {\n    @ApiModelProperty(value = \"Name of the band\", required = true)\n    private String name;\n    @ApiModelProperty(value = \"Year of the foundation\", required = true)\n    private int foundation;\n\n    public String getName() {\n        return name;\n    }\n\n    public BandCreationDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public int getFoundation() {\n        return foundation;\n    }\n\n    public BandCreationDTO setFoundation(int foundation) {\n        this.foundation = foundation;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return MoreObjects.toStringHelper(this)\n                .add(\"name\", name)\n                .add(\"foundation\", foundation)\n                .toString();\n    }\n}\n"
  },
  {
    "path": "rest-api-doc-jaxrs-swagger-asciidoc/src/main/java/de/philipphauer/blog/resources/dto/BandRetrievalDTO.java",
    "content": "package de.philipphauer.blog.resources.dto;\n\nimport com.google.common.base.MoreObjects;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\n\nimport java.util.UUID;\n\n@ApiModel(\"Payload for band retrieval\")\npublic class BandRetrievalDTO {\n    @ApiModelProperty(value = \"UUID\", required = true)\n    private UUID id;\n    @ApiModelProperty(value = \"Name of the band\", required = true)\n    private String name;\n    @ApiModelProperty(value = \"Year of the foundation\", required = true)\n    private int foundation;\n\n    public BandRetrievalDTO setId(UUID id) {\n        this.id = id;\n        return this;\n    }\n\n    public UUID getId() {\n        return id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public BandRetrievalDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public int getFoundation() {\n        return foundation;\n    }\n\n    public BandRetrievalDTO setFoundation(int foundation) {\n        this.foundation = foundation;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return MoreObjects.toStringHelper(this)\n                .add(\"id\", id)\n                .add(\"name\", name)\n                .add(\"foundation\", foundation)\n                .toString();\n    }\n}\n"
  },
  {
    "path": "rest-api-doc-jaxrs-swagger-asciidoc/src/main/resources/banner.txt",
    "content": "================================================================================\n\n              rest-api-doc-jaxrs-swagger-asciidoc\n\n================================================================================\n\n"
  },
  {
    "path": "rest-api-doc-jaxrs-swagger-asciidoc/swagger-ui/docker-compose.yml",
    "content": "version: '2'\nservices:\n  swagger_ui:\n    image: swaggerapi/swagger-ui\n    ports:\n      - \"8090:8080\""
  },
  {
    "path": "sealedclasses/.gitignore",
    "content": ".idea/\n*.iml\ntarget"
  },
  {
    "path": "sealedclasses/docker-compose.yml",
    "content": "version: '3'\nservices:\n  service-stub:\n    build: ./service-stub/\n    ports:\n      - \"5000:5000\""
  },
  {
    "path": "sealedclasses/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.phauer</groupId>\n    <artifactId>sealedclasses</artifactId>\n    <version>1.0-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>com.phauer sealedclasses</name>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <kotlin.version>1.3.20</kotlin.version>\n        <kotlin.code.style>official</kotlin.code.style>\n        <junit.version>4.12</junit.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.jetbrains.kotlin</groupId>\n            <artifactId>kotlin-stdlib</artifactId>\n            <version>${kotlin.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>slf4j-api</artifactId>\n            <version>1.7.25</version>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-web</artifactId>\n            <version>5.0.10.RELEASE</version>\n        </dependency>\n        <dependency>\n            <groupId>com.fasterxml.jackson.module</groupId>\n            <artifactId>jackson-module-kotlin</artifactId>\n            <version>2.9.5</version>\n        </dependency>\n        <dependency>\n            <groupId>org.jetbrains.kotlin</groupId>\n            <artifactId>kotlin-test-junit</artifactId>\n            <version>${kotlin.version}</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <version>${junit.version}</version>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <sourceDirectory>src/main/kotlin</sourceDirectory>\n        <testSourceDirectory>src/test/kotlin</testSourceDirectory>\n\n        <plugins>\n            <plugin>\n                <groupId>org.jetbrains.kotlin</groupId>\n                <artifactId>kotlin-maven-plugin</artifactId>\n                <version>${kotlin.version}</version>\n                <configuration>\n                    <jvmTarget>1.8</jvmTarget>\n                </configuration>\n                <executions>\n                    <execution>\n                        <id>compile</id>\n                        <phase>compile</phase>\n                        <goals>\n                            <goal>compile</goal>\n                        </goals>\n                    </execution>\n                    <execution>\n                        <id>test-compile</id>\n                        <phase>test-compile</phase>\n                        <goals>\n                            <goal>test-compile</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "sealedclasses/service-stub/Dockerfile",
    "content": "FROM python:3.6.4-alpine3.7\n\nRUN pip install --ignore-installed \"flask==1.0.1\"\n\nCOPY service-stub.py /\nCMD python3 /service-stub.py"
  },
  {
    "path": "sealedclasses/service-stub/service-stub.py",
    "content": "#!/usr/bin/env python3\nimport json\nfrom flask import Flask, Response\n\napp = Flask(__name__)\n\n\n@app.route('/userProfiles/<path:user_id>')\ndef get_design_data(user_id):\n    response = {\n        \"id\": user_id,\n        \"name\": \"Peter\",\n        \"avatarUrl\": \"http://localhost/peter.jpg\"\n    }\n    return Response(json.dumps(response), mimetype='application/json')\n\n\nif __name__ == '__main__':\n    # nice: in debug mode, flask detects changes in this file. no need to restart the process!\n    # 0.0.0.0 -> make server accessible externally (important for docker)\n    app.run(debug=True, port=5000, host='0.0.0.0')\n"
  },
  {
    "path": "sealedclasses/src/main/kotlin/com/phauer/HttpUserProfileClient.kt",
    "content": "package com.phauer\n\nimport com.phauer.common.Outcome\nimport com.phauer.common.exhaustive\nimport com.phauer.common.restTemplate\nimport org.springframework.web.client.RestClientException\nimport org.springframework.web.client.RestClientResponseException\nimport org.springframework.web.client.RestTemplate\nimport org.springframework.web.client.getForObject\nimport java.io.IOException\n\n/**\n */\nclass HttpUserProfileClient(\n    private val restTemplate: RestTemplate\n) {\n    /**\n     * Version 1: With Exceptions\n     */\n    @Throws(UserProfileClientException::class) // this (or javadoc) may help to document the exception...\n    fun requestUserProfile1(userId: String): UserProfileDTO = try {\n        restTemplate.getForObject<UserProfileDTO>(\"http://localhost:5000/userProfiles/$userId\")!!\n    } catch (ex: IOException) {\n        throw UserProfileClientException(\n            message = \"Server request failed due to an IO exception. Id: $userId, Message: ${ex.message}\",\n            cause = ex\n        )\n    } catch (ex: RestClientException) {\n        throw UserProfileClientException(\n            message = \"Server request failed. Id: $userId. status code: ${(ex as? RestClientResponseException)?.rawStatusCode}. body: ${(ex as? RestClientResponseException)?.responseBodyAsString}\",\n            cause = ex\n        )\n    }\n\n    /**\n     * Version 2: With Sealed Classes (domain-specific result class)\n     */\n    fun requestUserProfile2(userId: String): UserProfileResult = try {\n        val userProfile =\n            restTemplate.getForObject<UserProfileDTO>(\"http://localhost:5000/userProfiles/$userId\")!!\n        UserProfileResult.Success(userProfile = userProfile)\n    } catch (ex: IOException) {\n        UserProfileResult.Error(\n            message = \"Server request failed due to an IO exception. Id: $userId, Message: ${ex.message}\",\n            cause = ex\n        )\n    } catch (ex: RestClientException) {\n        UserProfileResult.Error(\n            message = \"Server request failed. Id: $userId. status code: ${(ex as? RestClientResponseException)?.rawStatusCode}. body: ${(ex as? RestClientResponseException)?.responseBodyAsString}\",\n            cause = ex\n        )\n    }\n\n\n    /**\n     * Version 3: With Sealed Classes (generic Result classes)\n     */\n    fun requestUserProfile3(userId: String): Outcome<UserProfileDTO> = try {\n        val userProfile =\n            restTemplate.getForObject<UserProfileDTO>(\"http://localhost:5000/userProfiles/$userId\")!!\n        Outcome.Success(value = userProfile)\n    } catch (ex: IOException) {\n        Outcome.Error(\n            message = \"Server request failed due to an IO exception. Id: $userId, Message: ${ex.message}\",\n            cause = ex\n        )\n    } catch (ex: RestClientException) {\n        Outcome.Error(\n            message = \"Server request failed. Id: $userId. status code: ${(ex as? RestClientResponseException)?.rawStatusCode}. body: ${(ex as? RestClientResponseException)?.responseBodyAsString}\",\n            cause = ex\n        )\n    }\n\n    fun downloadImage1(avatarUrl: String): ByteArray {\n        return ByteArray(0)\n    }\n\n    fun downloadImage2(avatarUrl: String): ImageDownloadResult {\n        return ImageDownloadResult.Success(ByteArray(0))\n    }\n}\n\nclass UserProfileClientException(message: String, cause: Exception? = null) : RuntimeException(message, cause)\nclass ImageDownloadException(message: String, cause: Exception? = null) : RuntimeException(message, cause)\nclass SuspiciousException(message: String, cause: Exception? = null) : RuntimeException(message, cause)\n\nsealed class UserProfileResult {\n    data class Success(val userProfile: UserProfileDTO) : UserProfileResult()\n    data class Error(val message: String, val cause: Exception? = null) : UserProfileResult()\n}\n\nsealed class ImageDownloadResult {\n    data class Success(val image: ByteArray) : ImageDownloadResult()\n    data class Error(val message: String, val cause: Exception? = null) : ImageDownloadResult()\n}\n\ndata class UserProfileDTO(\n    val id: String,\n    val name: String,\n    val avatarUrl: String\n)\n\nfun main() {\n    val client = HttpUserProfileClient(restTemplate())\n    val userId = \"1\"\n\n    /*\n     * Version 1\n     */\n    val avatarUrl = try {\n        client.requestUserProfile1(userId).avatarUrl\n    } catch (ex: UserProfileClientException) {\n        \"http://localhost/defaultAvatar.png\"\n    }\n\n    try {\n        val result = client.requestUserProfile1(userId)\n        processUserProfile(result)\n    } catch (ex: UserProfileClientException) {\n        queueDesignForRetry(userId, ex.message!!)\n    }\n\n    /*\n     * Version 2\n     */\n    val avatarUrl2 = when (val result = client.requestUserProfile2(userId)) {\n        is UserProfileResult.Success -> result.userProfile.avatarUrl\n        is UserProfileResult.Error -> \"http://localhost/defaultAvatar.png\"\n    }\n\n    when (val result = client.requestUserProfile2(userId)) {\n        is UserProfileResult.Success -> processUserProfile(result.userProfile)\n        is UserProfileResult.Error -> queueDesignForRetry(userId, result.message)\n    }.exhaustive\n\n    /*\n     * Version 3\n     */\n    val avatarUrl3 = when (val result = client.requestUserProfile3(userId)) {\n        is Outcome.Success -> result.value.avatarUrl\n        is Outcome.Error -> \"http://localhost/defaultAvatar.png\"\n    }\n\n    when (val result = client.requestUserProfile3(userId)) {\n        is Outcome.Success -> processUserProfile(result.value)\n        is Outcome.Error -> queueDesignForRetry(userId, result.message)\n    }.exhaustive\n\n\n    /*\n     Readability\n     Safety (force to handle error case. adding new error type)\n     Easy to Use (compiler guides us)\n     less-error prone (no uncaught runtime exceptions, what about adding new exception types?)\n     FP-compatibility (no side-effects)\n     */\n\n\n}\n\nfun aMoreComplicatedExample() {\n    val client = HttpUserProfileClient(restTemplate())\n    val userId = \"1\"\n\n    try {\n        val profile = client.requestUserProfile1(userId)\n        try {\n            val image = client.downloadImage1(profile.avatarUrl)\n            processImage(image)\n        } catch (ex: ImageDownloadException) {\n            queueForRetry(userId, ex.message)\n        }\n    } catch (ex: UserProfileClientException) {\n        showMessageToUser(userId, ex.message)\n    } catch (ex: SuspiciousException) {\n        // which method throws this exception?\n        // requestUserProfile1()? downloadImage1()? processImage()? queueForRetry()?\n    }\n    // have we forgot to catch an exception? Who knows.\n\n\n    // vs\n\n    when (val profileResult = client.requestUserProfile2(userId)) {\n        is UserProfileResult.Success -> {\n            when (val imageResult = client.downloadImage2(profileResult.userProfile.avatarUrl)) {\n                is ImageDownloadResult.Success -> processImage(imageResult.image)\n                is ImageDownloadResult.Error -> queueForRetry(userId, imageResult.message)\n            }\n        }\n        is UserProfileResult.Error -> showMessageToUser(userId, profileResult.message)\n    }\n\n\n}\n\nfun showMessageToUser(userId: String, message: String?) {\n\n\n}\n\nfun queueForRetry(userId: String, message: String?) {\n\n}\n\nfun processImage(image: ByteArray) {\n\n}\n\n\nfun queueDesignForRetry(designId: String, errorMessage: String) {\n    println(\"queueJobForRetry $designId\")\n    println(errorMessage)\n}\n\nfun processUserProfile(value: UserProfileDTO) {\n    println(\"processUserProfile $value\")\n}\n\n"
  },
  {
    "path": "sealedclasses/src/main/kotlin/com/phauer/ImageAvailabilityClient.kt",
    "content": "package com.phauer\n\nimport com.phauer.common.restTemplate\nimport org.springframework.web.client.HttpClientErrorException\nimport org.springframework.web.client.ResourceAccessException\nimport org.springframework.web.client.RestClientException\nimport org.springframework.web.client.RestTemplate\n\n/**\n * enum as a result can also be fine.\n */\nclass ImageAvailabilityClient(\n    private val restTemplate: RestTemplate\n) {\n    fun checkAvailabilityState(imageUrl: String): ImageAvailabilityState = try {\n        restTemplate.headForHeaders(imageUrl)\n        ImageAvailabilityState.OK\n    } catch (exception: HttpClientErrorException) {\n        ImageAvailabilityState.UNAVAILABLE\n    } catch (exception: ResourceAccessException) {\n        ImageAvailabilityState.TEMPORARY_UNAVAILABLE\n    } catch (exception: RestClientException) {\n        ImageAvailabilityState.UNAVAILABLE\n    }\n}\n\nenum class ImageAvailabilityState {\n    OK,\n    TEMPORARY_UNAVAILABLE,\n    UNAVAILABLE\n}\n\nfun main() {\n    val client = ImageAvailabilityClient(restTemplate())\n    val imageUrl = \"http://localhost/dog.jpg\"\n    when (client.checkAvailabilityState(imageUrl)) {\n        ImageAvailabilityState.OK -> markImageAsAvailable(imageUrl)\n        ImageAvailabilityState.UNAVAILABLE -> markImageAsUnavailable(imageUrl)\n        ImageAvailabilityState.TEMPORARY_UNAVAILABLE -> {\n            // do nothing right now and just wait for next execution cycle\n        }\n    }\n}\n\nfun markImageAsUnavailable(imageUrl: Any) {\n\n}\n\nfun markImageAsAvailable(imageUrl: Any) {\n\n}\n"
  },
  {
    "path": "sealedclasses/src/main/kotlin/com/phauer/LdapDAO.kt",
    "content": "package com.phauer\n\nimport com.phauer.AuthenticationResult.LdapGroup\nimport com.phauer.AuthenticationResult.Success\n\n/**\n * create tailored result object\n */\nclass LdapDAO(\n) {\n    fun authenticate(username: String, password: String): AuthenticationResult {\n        return Success(LdapGroup.READONLY)\n    }\n}\n\nsealed class AuthenticationResult {\n    data class Success(val group: LdapGroup) : AuthenticationResult()\n    data class Failure(val reason: FailureReason) : AuthenticationResult()\n    enum class FailureReason {\n        BLANK_USER_OR_PW,\n        INVALID_USER_OR_PW,\n        USER_IS_NOT_IN_GROUP,\n        CONNECTION_ISSUES\n    }\n\n    enum class LdapGroup {\n        READONLY,\n        NORMAL,\n        ADMIN\n    }\n}"
  },
  {
    "path": "sealedclasses/src/main/kotlin/com/phauer/common/Common.kt",
    "content": "package com.phauer.common\n\nimport com.fasterxml.jackson.annotation.JsonInclude\nimport com.fasterxml.jackson.databind.DeserializationFeature\nimport com.fasterxml.jackson.databind.ObjectMapper\nimport com.fasterxml.jackson.module.kotlin.registerKotlinModule\nimport org.springframework.http.converter.BufferedImageHttpMessageConverter\nimport org.springframework.http.converter.ByteArrayHttpMessageConverter\nimport org.springframework.http.converter.StringHttpMessageConverter\nimport org.springframework.http.converter.json.MappingJackson2HttpMessageConverter\nimport org.springframework.web.client.RestTemplate\nimport java.nio.charset.StandardCharsets\n\n/* The name \"Result\" conflicts with a class from the Kotlin std lib. */\nsealed class Outcome<out T : Any> {\n    data class Success<out T : Any>(val value: T) : Outcome<T>()\n    data class Error(val message: String, val cause: Exception? = null) : Outcome<Nothing>()\n}\n\nval <T> T.exhaustive: T\n    get() = this\n\n\nfun restTemplate(): RestTemplate {\n    val mapper = ObjectMapper().registerKotlinModule()\n    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)\n    mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL)\n\n    return RestTemplate().apply {\n        messageConverters = listOf(\n            MappingJackson2HttpMessageConverter(mapper),\n            StringHttpMessageConverter(StandardCharsets.UTF_8),\n            BufferedImageHttpMessageConverter(),\n            ByteArrayHttpMessageConverter()\n        )\n    }\n}"
  },
  {
    "path": "sealedclasses/src/test/kotlin/com/phauer/HelloTest.kt",
    "content": "package com.phauer\n\nimport org.junit.Test\nimport kotlin.test.assertEquals\n\nclass HelloTest {\n\n}\n"
  },
  {
    "path": "smooth-local-dev-docker/.gitignore",
    "content": ".idea/\ntarget/\n*.iml"
  },
  {
    "path": "smooth-local-dev-docker/Pipfile",
    "content": "[[source]]\n\nurl = \"https://pypi.python.org/simple\"\nverify_ssl = true\nname = \"pypi\"\n\n\n[packages]\n\nflask = \"==0.12.2\"\nFaker = \"==0.8.10\"\npymongo = \"==3.6.1\"\nmysql-connector = \"==2.1.6\"\n\n[dev-packages]\n\n[requires]\n\npython_version = \"3.6\"\n"
  },
  {
    "path": "smooth-local-dev-docker/bla.conf",
    "content": "ktor {\n    deployment {\n        port = 8080\n        watch = [ solutions/exercise4 ]\n    }\n\n    application {\n        modules = [ de.philipphauer.blog.MainKt.module ]\n    }\n}"
  },
  {
    "path": "smooth-local-dev-docker/docker-compose.yml",
    "content": "version: '3'\nservices:\n  mongo:\n    image: mongo:3.4.3\n    ports:\n      - \"27017:27017\"\n    command: --profile=1 --slowms=0\n  mongo_seeding:\n    build: ./local-dev/mongo-seeding\n    depends_on:\n      - mongo\n  mysql:\n    image: mysql:5.6.33\n    ports:\n      - \"3306:3306\"\n    environment:\n      MYSQL_ROOT_PASSWORD: \"root\"\n      MYSQL_DATABASE: \"database\"\n  mysql_seeding:\n    build: ./local-dev/mysql-seeding\n    depends_on:\n      - mysql\n  external_service_stub:\n    build: ./local-dev/external-service-stub\n    ports:\n      - \"5000:5000\"\n#  external_service_mongo:\n#    image: mongo:3.4.3\n#    ports:\n#      - \"27018:27017\"\n#    command: --profile=1 --slowms=0\n#  external_service_wrapped:\n#    build:\n#      context: local-dev/external-service-wrapped/\n#      args:\n#        SERVICE_VERSION: 2.13.13\n#    ports:\n#      - \"8080:8080\"\n#    depends_on:\n#      - external_service_mongo"
  },
  {
    "path": "smooth-local-dev-docker/local-dev/external-service-stub/Dockerfile",
    "content": "FROM python:3.6.4-alpine3.7\n\nRUN pip install pipenv\n\nCOPY Pipfile* /\nRUN pipenv install --deploy --system\n\nCOPY external-service-stub.py /\nCOPY static-user-response.json /static-user-response.json\nCMD python3 /external-service-stub.py\n"
  },
  {
    "path": "smooth-local-dev-docker/local-dev/external-service-stub/Pipfile",
    "content": "[[source]]\n\nurl = \"https://pypi.python.org/simple\"\nverify_ssl = true\nname = \"pypi\"\n\n\n[packages]\n\nflask = \"==0.12.2\"\nFaker = \"==0.8.10\"\n\n[dev-packages]\n\n[requires]\n\npython_version = \"3.6\"\n"
  },
  {
    "path": "smooth-local-dev-docker/local-dev/external-service-stub/external-service-stub.py",
    "content": "#!/usr/bin/env python3\nimport json\n\nfrom faker import Faker\nfrom flask import Flask, Response\n\napp = Flask(__name__)\nfaker = Faker('en')\n\n\n# A: Generate the payload with faker\n@app.route('/users', methods=['GET'])\ndef get_users_faker():\n    response_users = [generate_user(user_id) for user_id in range(50)]\n    payload = {\n        'users': response_users,\n        'size': len(response_users)\n    }\n    return Response(json.dumps(payload), mimetype='application/json')\n\n\ndef generate_user(user_id):\n    return {\n        'id': user_id,\n        'email': faker.email(),\n        'name': faker.name(),\n        'address': faker.address(),\n        'company': faker.company(),\n        'keyAccountInfo': faker.sentence(nb_words=6) if faker.boolean(chance_of_getting_true=50) else None\n    }\n\n\n# B: Return a static payload\n@app.route('/users2', methods=['GET'])\ndef get_users_static():\n    with open('static-user-response.json', 'r') as payload_file:\n        return Response(payload_file.read(), mimetype='application/json')\n\n\napp.run(debug=False, port=5000, host='0.0.0.0')\n"
  },
  {
    "path": "smooth-local-dev-docker/local-dev/external-service-stub/static-user-response.json",
    "content": "{\n  \"size\": 50,\n  \"users\": [\n    {\n      \"company\": \"Powers Inc\",\n      \"email\": \"ihouston@garcia.com\",\n      \"id\": 0,\n      \"key_account_info\": null,\n      \"name\": \"Shannon Moore\"\n    },\n    {\n      \"company\": \"Stark-Sullivan\",\n      \"email\": \"katie55@wood.com\",\n      \"id\": 1,\n      \"key_account_info\": null,\n      \"name\": \"Jeffery Sloan\"\n    },\n    {\n      \"company\": \"Garza, Gonzalez and Phillips\",\n      \"email\": \"wheath@roberts-owens.com\",\n      \"id\": 2,\n      \"key_account_info\": \"This is an important account!\",\n      \"name\": \"Nicole Collins\"\n    }\n  ]\n}"
  },
  {
    "path": "smooth-local-dev-docker/local-dev/external-service-wrapped/Dockerfile",
    "content": "FROM openjdk:8u151-jre-alpine3.7\n\nRUN apk add --no-cache curl\n\n# default build argument. overwritten in docker-compose.yml\nARG SERVICE_VERSION=2.13.13\nRUN curl --user nexusUser:nexusPassword --output external-service.jar https://my-nexus-repo.com/repository/maven-public/de/philipphauer/blog/external-service/$SERVICE_VERSION/external-service-$SERVICE_VERSION.jar\n\nCOPY config.yaml /\nCMD java -jar external-service.jar --spring.config.location config.yaml\n"
  },
  {
    "path": "smooth-local-dev-docker/local-dev/external-service-wrapped/config.yaml",
    "content": "server:\n  port: 8080\n  data:\n    mongodb:\n      # the host is the service name in the docker-compose.yml.\n      uri: \"mongodb://user:password@external_service_mongo:27018/test\"\n"
  },
  {
    "path": "smooth-local-dev-docker/local-dev/mongo-seeding/Dockerfile",
    "content": "FROM python:3.6.4-alpine3.7\n\nRUN pip install pipenv\n\nCOPY Pipfile* /\nRUN pipenv install --deploy --system\n\nCOPY seed-mongo.py /\nCMD python3 /seed-mongo.py\n"
  },
  {
    "path": "smooth-local-dev-docker/local-dev/mongo-seeding/Pipfile",
    "content": "[[source]]\n\nurl = \"https://pypi.python.org/simple\"\nverify_ssl = true\nname = \"pypi\"\n\n\n[packages]\n\npymongo = \"==3.6.1\"\nFaker = \"==0.8.10\"\n\n[dev-packages]\n\n[requires]\n\npython_version = \"3.6\"\n"
  },
  {
    "path": "smooth-local-dev-docker/local-dev/mongo-seeding/seed-mongo.py",
    "content": "#!/usr/bin/env python3.6\n\nimport random\nfrom typing import List\n\nfrom bson import ObjectId\nfrom faker import Faker\nfrom pymongo import MongoClient\n\nPOSSIBLE_STATES = ['ACTIVE', 'INACTIVE']\nPOSSIBLE_TAGS = ['vacation', 'business', 'technology', 'mobility', 'apparel']\nfaker = Faker('en')\n\n\nclass MongoSeeder:\n\n    def __init__(self):\n        host = 'mongo' if script_runs_within_container() else 'localhost'\n        client = MongoClient(f'mongodb://{host}:27017/test')\n        self.db = client.test\n\n    def seed(self):\n        print('Clearing collection...')\n        self.db.designs.remove({})\n        print('Inserting new designs...')\n        designs = [generate_design() for _ in range(100)]\n        self.db.designs.insert_many(designs)\n        print('Done.')\n\n\ndef generate_design():\n    data = {\n        '_id': ObjectId()\n        , 'name': faker.word()\n        , 'description': faker.sentence(nb_words=7)\n        , 'date': faker.date_time()\n        , 'tags': choose_max_n_times(possibilities=POSSIBLE_TAGS, max_n=3)\n        , 'state': random.choice(POSSIBLE_STATES)\n        , 'designer': {\n            'id': random.randint(0, 999999)\n            , 'name': faker.name()\n            , 'address': faker.address()\n        }\n    }\n    if faker.boolean(chance_of_getting_true=50):\n        data['superDesign'] = True\n    return data\n\n\ndef script_runs_within_container():\n    with open('/proc/1/cgroup', 'r') as cgroup_file:\n        return 'docker' in cgroup_file.read()\n\n\ndef choose_max_n_times(possibilities: List, max_n: int) -> List:\n    copied_list = list(possibilities)\n    random.shuffle(copied_list)\n    n = random.randint(0, max_n)\n    chosen = [copied_list.pop() for _ in range(n)]\n    return chosen\n\n\nMongoSeeder().seed()\n\n"
  },
  {
    "path": "smooth-local-dev-docker/local-dev/mysql-seeding/Dockerfile",
    "content": "FROM python:3.6.4-alpine3.7\n\nRUN pip install pipenv\n\nCOPY Pipfile* /\nRUN pipenv install --deploy --system\n\nCOPY seed-mysql.py /\nCMD python3 /seed-mysql.py\n"
  },
  {
    "path": "smooth-local-dev-docker/local-dev/mysql-seeding/Pipfile",
    "content": "[[source]]\n\nurl = \"https://pypi.python.org/simple\"\nverify_ssl = true\nname = \"pypi\"\n\n\n[packages]\n\nmysql-connector = \"==2.1.6\"\nFaker = \"==0.8.10\"\n\n[dev-packages]\n\n[requires]\n\npython_version = \"3.6\"\n"
  },
  {
    "path": "smooth-local-dev-docker/local-dev/mysql-seeding/seed-mysql.py",
    "content": "#!/usr/bin/env python3.6\nimport random\nimport time\n\nimport mysql.connector\nfrom faker import Faker\nfrom mysql.connector import InterfaceError\n\nPOSSIBLE_STATES = ['ACTIVE', 'INACTIVE']\nfaker = Faker('en')\n\n\nclass MySqlSeeder:\n\n    def __init__(self):\n        config = {\n            'user': 'root',\n            'password': 'root',\n            'host': 'mysql' if script_runs_within_container() else 'localhost',\n            'port': '3306',\n            'database': 'database'\n        }\n        while not hasattr(self, 'connection'):\n            try:\n                self.connection = mysql.connector.connect(**config)\n                self.cursor = self.connection.cursor()\n            except InterfaceError:\n                print(\"MySQL Container has not started yet. Sleep and retry...\")\n                time.sleep(1)\n\n    def seed(self):\n        print(\"Clearing old data...\")\n        self.drop_user_table()\n        print(\"Start seeding...\")\n        self.create_user_table()\n        self.insert_users()\n\n        self.connection.commit()\n        self.cursor.close()\n        self.connection.close()\n        print(\"Done\")\n\n    def create_user_table(self):\n        sql = '''\n        CREATE TABLE users(\n          id INT PRIMARY KEY AUTO_INCREMENT,\n          name VARCHAR(50),\n          state VARCHAR(50),\n          birthday TIMESTAMP,\n          notes VARCHAR(150),\n          is_adult TINYINT(1)\n        );\n        '''\n        self.cursor.execute(sql)\n\n    def insert_users(self):\n        for _ in range(300):\n            sql = '''\n            INSERT INTO users (name, state, birthday, notes, is_adult)\n            VALUES (%(name)s, %(state)s, %(birthday)s, %(notes)s, %(is_adult)s);\n            '''\n            user_data = {\n                'name': faker.name(),\n                'state': random.choice(POSSIBLE_STATES),\n                'birthday': faker.date_time(),\n                'notes': faker.sentence(nb_words=5),\n                'is_adult': faker.boolean(chance_of_getting_true=80)\n            }\n            self.cursor.execute(sql, user_data)\n\n    def drop_user_table(self):\n        self.cursor.execute('DROP TABLE IF EXISTS users;')\n\n\ndef script_runs_within_container():\n    with open('/proc/1/cgroup', 'r') as cgroup_file:\n        return 'docker' in cgroup_file.read()\n\n\nMySqlSeeder().seed()\n"
  },
  {
    "path": "smooth-local-dev-docker/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>de.philipphauer.blog</groupId>\n    <artifactId>smooth-local-dev-docker</artifactId>\n    <version>1.0-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <kotlin.version>1.2.30</kotlin.version>\n        <junit.version>4.12</junit.version>\n        <ktor.version>0.9.1</ktor.version>\n        <kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>\n        <kotlin.compiler.incremental>false</kotlin.compiler.incremental>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>io.ktor</groupId>\n            <artifactId>ktor-server-netty</artifactId>\n            <version>${ktor.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>io.ktor</groupId>\n            <artifactId>ktor-server-core</artifactId>\n            <version>${ktor.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>io.ktor</groupId>\n            <artifactId>ktor-client-cio</artifactId>\n            <version>${ktor.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.jetbrains.kotlin</groupId>\n            <artifactId>kotlin-stdlib</artifactId>\n            <version>${kotlin.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>slf4j-simple</artifactId>\n            <version>1.7.25</version>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.data</groupId>\n            <artifactId>spring-data-mongodb</artifactId>\n            <version>2.0.6.RELEASE</version>\n        </dependency>\n        <dependency>\n            <groupId>com.fasterxml.jackson.module</groupId>\n            <artifactId>jackson-module-kotlin</artifactId>\n            <version>2.9.5</version>\n        </dependency>\n        <dependency>\n            <groupId>org.jetbrains.kotlin</groupId>\n            <artifactId>kotlin-test-junit</artifactId>\n            <version>${kotlin.version}</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <version>${junit.version}</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n            <version>2.0.1.RELEASE</version>\n        </dependency>\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>6.0.6</version>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <sourceDirectory>src/main/kotlin</sourceDirectory>\n        <testSourceDirectory>src/test/kotlin</testSourceDirectory>\n\n        <plugins>\n            <plugin>\n                <groupId>org.jetbrains.kotlin</groupId>\n                <artifactId>kotlin-maven-plugin</artifactId>\n                <version>${kotlin.version}</version>\n                <configuration>\n                    <jvmTarget>1.8</jvmTarget>\n                    <args>\n                        <arg>-Xcoroutines=enable</arg>\n                    </args>\n                </configuration>\n                <executions>\n                    <execution>\n                        <id>compile</id>\n                        <phase>compile</phase>\n                        <goals>\n                            <goal>compile</goal>\n                        </goals>\n                    </execution>\n                    <execution>\n                        <id>test-compile</id>\n                        <phase>test-compile</phase>\n                        <goals>\n                            <goal>test-compile</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n    <repositories>\n        <repository>\n            <id>ktor</id>\n            <url>http://dl.bintray.com/kotlin/ktor</url>\n        </repository>\n        <repository>\n            <id>kotlinx</id>\n            <url>http://dl.bintray.com/kotlin/kotlinx</url>\n        </repository>\n        <repository>\n            <id>jcenter</id>\n            <url>http://jcenter.bintray.com</url>\n        </repository>\n    </repositories>\n\n</project>\n"
  },
  {
    "path": "smooth-local-dev-docker/src/main/kotlin/de/philipphauer/blog/ExternalServiceUserClient.kt",
    "content": "package de.philipphauer.blog\n\nimport com.fasterxml.jackson.databind.ObjectMapper\nimport com.fasterxml.jackson.module.kotlin.registerKotlinModule\nimport io.ktor.client.HttpClient\nimport io.ktor.client.engine.cio.CIO\nimport io.ktor.client.request.get\n\nclass ExternalServiceUserClient {\n\n    private val client = HttpClient(CIO)\n    private val mapper = ObjectMapper().registerKotlinModule()\n\n    suspend fun findUsers(): List<UserDTO> {\n        val json = client.get<String>(\"http://localhost:5000/users\")\n        val userResponse = mapper.readValue(json, UserResponseDTO::class.java)\n        return userResponse.users\n    }\n}\n\ndata class UserResponseDTO(\n    val size: Int,\n    val users: List<UserDTO>\n)\n\ndata class UserDTO(\n    val address: String,\n    val company: String,\n    val email: String,\n    val id: Int,\n    val keyAccountInfo: String?,\n    val name: String\n)\n"
  },
  {
    "path": "smooth-local-dev-docker/src/main/kotlin/de/philipphauer/blog/Main.kt",
    "content": "package de.philipphauer.blog\n\nimport com.fasterxml.jackson.databind.ObjectMapper\nimport com.fasterxml.jackson.module.kotlin.registerKotlinModule\nimport io.ktor.application.Application\nimport io.ktor.application.call\nimport io.ktor.application.install\nimport io.ktor.features.CallLogging\nimport io.ktor.features.DefaultHeaders\nimport io.ktor.http.ContentType\nimport io.ktor.response.respondText\nimport io.ktor.routing.Routing\nimport io.ktor.routing.get\nimport io.ktor.server.engine.embeddedServer\nimport io.ktor.server.netty.Netty\n\nfun Application.module() {\n    val designDao = MongoDesignDAO()\n    val userDao = MySqlUserDAO()\n    val userClient = ExternalServiceUserClient()\n\n    install(DefaultHeaders)\n    install(CallLogging)\n    install(Routing) {\n        get(\"/designs\") {\n            val designs = designDao.findDesigns()\n            call.respondText(text = designs.toJson(), contentType = ContentType.Application.Json)\n        }\n        get(\"/users\") {\n            val users = userDao.findUsers()\n            call.respondText(text = users.toJson(), contentType = ContentType.Application.Json)\n        }\n        get(\"/users_ext\") {\n            val users = userClient.findUsers()\n            call.respondText(text = users.toJson(), contentType = ContentType.Application.Json)\n        }\n    }\n}\n\nfun main(args: Array<String>) {\n    embeddedServer(\n        Netty,\n        port = 8080\n        , watchPaths = listOf(\"target\")\n        , module = Application::module\n    ).start(wait = true)\n}\n\nval mapper = ObjectMapper().registerKotlinModule()\nprivate fun Any.toJson() = mapper.writeValueAsString(this)\n"
  },
  {
    "path": "smooth-local-dev-docker/src/main/kotlin/de/philipphauer/blog/MongoDesignDAO.kt",
    "content": "package de.philipphauer.blog\n\nimport com.mongodb.MongoClientURI\nimport org.bson.types.ObjectId\nimport org.springframework.data.mongodb.core.MongoTemplate\nimport org.springframework.data.mongodb.core.SimpleMongoDbFactory\nimport org.springframework.data.mongodb.core.mapping.Document\nimport org.springframework.data.mongodb.core.mapping.Field\nimport java.time.Instant\n\nclass MongoDesignDAO {\n\n    private val dbFactory = SimpleMongoDbFactory(MongoClientURI(\"mongodb://localhost:27017/test\"))\n    private val template = MongoTemplate(dbFactory)\n\n    fun findDesigns(): List<Design> = template.findAll(Design::class.java)\n}\n\n@Document(collection = \"designs\")\ndata class Design(\n    val id: ObjectId,\n    val name: String,\n    val description: String,\n    val date: Instant,\n    val tags: List<String>,\n    val state: String,\n    val designer: Designer\n)\n\ndata class Designer(\n    @field:Field(\"id\")\n    val id: Int,\n    val name: String,\n    val address: String\n)"
  },
  {
    "path": "smooth-local-dev-docker/src/main/kotlin/de/philipphauer/blog/MySqlUserDAO.kt",
    "content": "package de.philipphauer.blog\n\nimport org.springframework.boot.jdbc.DataSourceBuilder\nimport org.springframework.jdbc.core.JdbcTemplate\nimport java.sql.ResultSet\nimport java.time.Instant\n\nclass MySqlUserDAO {\n\n    private val dataSource = DataSourceBuilder.create()\n        .username(\"root\")\n        .password(\"root\")\n        .url(\"jdbc:mysql://localhost:3306/database\")\n        .driverClassName(\"com.mysql.cj.jdbc.Driver\")\n        .build()\n    private val template = JdbcTemplate(dataSource)\n\n    fun findUsers() = template.query(\"SELECT * FROM users;\", this::mapToUser)\n\n    private fun mapToUser(rs: ResultSet, rowNum: Int) = User(\n        id = rs.getInt(\"id\")\n        , name = rs.getString(\"name\")\n        , state = State.valueOf(rs.getString(\"state\"))\n        , birthday = rs.getTimestamp(\"birthday\").toInstant()\n        , notes = rs.getString(\"notes\")\n        , adult = rs.getBoolean(\"is_adult\")\n    )\n}\n\ndata class User(\n    val id: Int,\n    val name: String,\n    val state: State,\n    val birthday: Instant,\n    val notes: String,\n    val adult: Boolean\n)\n\nenum class State { ACTIVE, INACTIVE }\n"
  },
  {
    "path": "smooth-local-dev-docker/src/test/kotlin/de/philipphauer/blog/HelloTest.kt",
    "content": "package de.philipphauer.blog\n\nimport org.junit.Test\nimport kotlin.test.assertEquals\n\nclass HelloTest {\n\n}\n"
  },
  {
    "path": "testingrestservice/integration-tests/.gitignore",
    "content": ".idea\n*.iml\ntarget\n"
  },
  {
    "path": "testingrestservice/integration-tests/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>de.philipphauer.blog.testingrestservice</groupId>\n    <artifactId>integration-tests</artifactId>\n    <version>1.0-SNAPSHOT</version>\n\n    <properties>\n        <java.version>1.8</java.version>\n        <kotlin.version>1.0.5-2</kotlin.version>\n    </properties>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.jetbrains.kotlin</groupId>\n                <artifactId>kotlin-maven-plugin</artifactId>\n                <version>${kotlin.version}</version>\n                <executions>\n                    <execution>\n                        <id>compile</id>\n                        <phase>compile</phase>\n                        <goals>\n                            <goal>compile</goal>\n                        </goals>\n                    </execution>\n                    <execution>\n                        <id>test-compile</id>\n                        <phase>test-compile</phase>\n                        <goals>\n                            <goal>test-compile</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <version>3.5</version>\n                <executions>\n                    <execution>\n                        <id>compile</id>\n                        <phase>compile</phase>\n                        <goals>\n                            <goal>compile</goal>\n                        </goals>\n                    </execution>\n                    <execution>\n                        <id>testCompile</id>\n                        <phase>test-compile</phase>\n                        <goals>\n                            <goal>testCompile</goal>\n                        </goals>\n                    </execution>\n                </executions>\n                <configuration>\n                    <source>${java.version}</source>\n                    <target>${java.version}</target>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n\n    <dependencies>\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <version>4.12</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.assertj</groupId>\n            <artifactId>assertj-core</artifactId>\n            <version>1.7.1</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.jayway.restassured</groupId>\n            <artifactId>rest-assured</artifactId>\n            <version>2.8.0</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.jayway.awaitility</groupId>\n            <artifactId>awaitility</artifactId>\n            <version>1.7.0</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.fasterxml.jackson.core</groupId>\n            <artifactId>jackson-databind</artifactId>\n            <version>2.9.10.1</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.hamcrest</groupId>\n            <artifactId>hamcrest-core</artifactId>\n            <version>1.3</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.jetbrains.kotlin</groupId>\n            <artifactId>kotlin-stdlib</artifactId>\n            <version>${kotlin.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.jetbrains.kotlin</groupId>\n            <artifactId>kotlin-test</artifactId>\n            <version>${kotlin.version}</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.fasterxml.jackson.module</groupId>\n            <artifactId>jackson-module-kotlin</artifactId>\n            <version>2.8.4</version>\n        </dependency>\n    </dependencies>\n</project>"
  },
  {
    "path": "testingrestservice/integration-tests/src/test/java/de/philipphauer/blog/testingrestservice/integrationtests/BlogsTest.java",
    "content": "package de.philipphauer.blog.testingrestservice.integrationtests;\n\nimport com.jayway.awaitility.Duration;\nimport com.jayway.awaitility.core.ConditionFactory;\nimport com.jayway.restassured.builder.RequestSpecBuilder;\nimport com.jayway.restassured.filter.log.RequestLoggingFilter;\nimport com.jayway.restassured.filter.log.ResponseLoggingFilter;\nimport com.jayway.restassured.http.ContentType;\nimport com.jayway.restassured.path.json.JsonPath;\nimport com.jayway.restassured.specification.RequestSpecification;\nimport de.philipphauer.blog.testingrestservice.integrationtests.dto.BlogDTO;\nimport de.philipphauer.blog.testingrestservice.integrationtests.dto.BlogListDTO;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\n\nimport java.util.concurrent.TimeUnit;\n\nimport static com.jayway.awaitility.Awaitility.await;\nimport static com.jayway.restassured.RestAssured.given;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.hamcrest.Matchers.*;\n\npublic class BlogsTest {\n\n    //=> create test code that is readable and easy write. this way keep tests maintainable.\n\n\n    //use rest-assured (nice readable fluent api)\n    @Test\n    public void collectionResourceOK(){\n        given()\n                .param(\"limit\", 20)\n                .when()\n                .get(\"blogs\")\n                .then()\n                .statusCode(200);\n        //(import static com.jayway.restassured.RestAssured.given;)\n    }\n\n    String host = System.getProperty(\"host\");//better remove this lines for clearity\n    String port = System.getProperty(\"port\");\n\n    //use spec to reuse common request parameter\n    private static RequestSpecification spec;\n\n    @BeforeClass\n    public static void initSpec(){\n        spec = new RequestSpecBuilder()\n                .setContentType(ContentType.JSON)\n                .setBaseUri(\"http://localhost:8080/\")\n                .addFilter(new ResponseLoggingFilter())//log request and response for better debugging. You can also only log if a requests fails.\n                .addFilter(new RequestLoggingFilter())\n                .build();\n    }\n    @Test\n    public void useSpec(){\n        given()\n                .spec(spec)\n                .param(\"limit\", 20)\n                .when()\n                .get(\"blogs\")\n                .then()\n                .statusCode(200);\n    }\n\n    @Test\n    public void createBlogAndCheckExistence(){\n        //use POJOs/DTO to create dummy data and use serialization to get JSON. don't construct json manually.\n        //construction your json payload with javax.json.JsonObject doesn't make any fun at all. verbose, cumbersome, not type-safe and error-prone. instead use POJOs.\n\n        //POJOs:\n        //DON'T use ordinary setters -> verbose to write.\n        //<example code>\n        //DON'T use huge constructor with every possible field as an argument -> hard to read\n        BlogDTO blogDTO = new BlogDTO(\"Example\", \"Example\", \"www.blogdomain.de\");//which parameter means what? hard to read.\n        //better: use fluent setter in POJO -> readable! see meaning of every argument.\n        //use intelliJ's generator for setter (Alt+Insert > Getter and Setter) can be performed using only the keyboard.\n        //<POJO code>\n        BlogDTO newBlog = new BlogDTO()\n                .setName(\"Example\")\n                .setDescription(\"Example\")\n                .setUrl(\"www.blogdomain.de\");\n        //object mapping is built-in into rest-assured. only pass pojo to post. automatically serialized to json and put into the http body\n        //just add jackson as a project dependency -> rest-assured will automatically use it.\n        String locationHeader = given()\n                .spec(spec)\n                .body(newBlog)\n                .when()\n                .post(\"blogs\")\n                .then()\n                .statusCode(201)\n                .extract().header(\"location\");\n        //extract location header and check existence\n        //use POJO and deserialize respone json to your POJO again. use @JsonIgnoreProperties(ignoreUnknown = true) in pojo if necessary.\n        BlogDTO retrievedBlog = given()\n                .spec(spec)\n                .when()\n                .get(locationHeader)\n                .then()\n                .statusCode(200)\n                .extract().as(BlogDTO.class);\n        //using you POJOs (instead of JSONObject or Strings) makes equality check easy, typesafe and readeble.\n        //use assertj (nice fluent, powerful and typesafe testing api; produces readable failure messages). I like it much more than hamcrest (problem to find matcher that can be used with type XY).\n        assertThat(retrievedBlog.getName()).isEqualTo(newBlog.getName());\n        assertThat(retrievedBlog.getDescription()).isEqualTo(newBlog.getDescription());\n        assertThat(retrievedBlog.getUrl()).isEqualTo(newBlog.getUrl());\n        //import static org.assertj.core.api.Assertions.assertThat;\n\n        //even better:\n        assertThat(retrievedBlog).isEqualToIgnoringGivenFields(newBlog, \"id\");\n    }\n\n    //write clean test code: keep your test readable and maintainable! keep test methods short; use sub methods with nice descriptive names to make your test readable\n    //every time you start building block and override it with a comment -> extract the block to a method instead and use the comment as a method name. (Strg+Alt+M in IntelliJ)\n    @Test\n    public void createBlogAndCheckExistenceReadable(){\n        BlogDTO newBlog = createDummyBlog();\n        String blogResourceLocation = createResource(\"blogs\", newBlog);\n        BlogDTO retrievedBlog = getResource(blogResourceLocation, BlogDTO.class);\n        assertEqualBlog(newBlog, retrievedBlog);\n    }\n\n    private BlogDTO createDummyBlog() {\n        return new BlogDTO()\n                .setName(\"Example Name\")\n                .setDescription(\"Example Description\")\n                .setUrl(\"www.blogdomain.de\");\n    }\n\n    //nice reusable method\n    private String createResource(String path, Object bodyPayload) {\n        return given()\n                .spec(spec)\n                .body(bodyPayload)\n                .when()\n                .post(path)\n                .then()\n                .statusCode(201)\n                .extract().header(\"location\");\n    }\n\n    //nice reusable method\n    private <T> T getResource(String locationHeader, Class<T> responseClass) {\n        return given()\n                    .spec(spec)\n                    .when()\n                    .get(locationHeader)\n                    .then()\n                    .statusCode(200)\n                    .extract().as(responseClass);\n    }\n\n    private void assertEqualBlog(BlogDTO newBlog, BlogDTO retrievedBlog) {\n        assertThat(retrievedBlog.getName()).isEqualTo(newBlog.getName());\n        assertThat(retrievedBlog.getDescription()).isEqualTo(newBlog.getDescription());\n        assertThat(retrievedBlog.getUrl()).isEqualTo(newBlog.getUrl());\n    }\n\n    //use abstract test class (spec, generic methods (createResource() and getResource())\n\n    //use asserj's as() to add domain information to your assertion failure messages\n    private void assertEqualBlog2(BlogDTO newBlog, BlogDTO retrievedBlog) {\n        assertThat(retrievedBlog.getName()).as(\"Blog Name\").isEqualTo(newBlog.getName());\n        assertThat(retrievedBlog.getDescription()).as(\"Blog Description\").isEqualTo(newBlog.getDescription());\n        assertThat(retrievedBlog.getUrl()).as(\"Blog URL\").isEqualTo(newBlog.getUrl());\n    }\n\n    //I always create a POJO and map json to object. even when used just once for a request response.\n    //to simplify the creation of the POJO class, a) consider public fields (OMG!) b) only add the fields you are interested in (+@JsonIgnoreProperties(ignoreUnknown = true))\n    //<code BlogListDTO> vgl with json-response (with all fields)\n    //but: when you use class to create a dummy obj (e.g. to post it to the service) --> easier with fluent setter and private fields.\n    @Test\n    public void getAllBlogsWithMapping(){\n        BlogListDTO retrievedBlogs = given()\n                .spec(spec)\n                .when()\n                .get(\"blogs\")\n                .then()\n                .statusCode(200)\n                .extract().as(BlogListDTO.class);\n        assertThat(retrievedBlogs.count).isGreaterThan(7);\n        assertThat(retrievedBlogs.blogs).isNotEmpty();\n    }\n\n    //alternative to separate POJO class/Mapping:\n    // to extract simple things from response -> jsonpath\n    // (strings -> but not type-safe and error-prone). only for simple things.\n    @Test\n    public void getAllBlogsWithJsonPath(){\n        //A) using assertj (i prefer because easy to debug and readable; and typesafe, finding matcher)\n        JsonPath retrievedBlogs = given()\n                .spec(spec)\n                .when()\n                .get(\"blogs\")\n                .then()\n                .statusCode(200)\n                .extract().jsonPath();\n        assertThat(retrievedBlogs.getInt(\"count\")).isGreaterThan(5);\n        assertThat(retrievedBlogs.getList(\"blogs.id\")).isNotEmpty();\n\n        //B) using directly in rest-assured statement with hamcrest matcher. built-in hamcrest support in rest-assured. shorter&concise, you have to use hamcrest. ;-)\n        //not type-safe, error-prone, harder to debug\n        //import static org.hamcrest.Matchers.*;\n        given()\n                .spec(spec)\n                .when()\n                .get(\"blogs\")\n                .then()\n                .statusCode(200)\n                .content(\"count\", greaterThan(5))\n                .content(\"blogs\", is(not(empty())));\n    }\n\n    //more sophisticated assertions\n    @Test\n    public void createBlogAndCheckInList(){\n        BlogDTO newBlog = createDummyBlog();\n        String blogResourceLocation  = createResource(\"blogs\", newBlog);\n        int createdBlogId = extractId(blogResourceLocation);\n\n        //use assertj to make powerful assertions about the responded data (e.g. list containsId)\n        //a) object mapping + assertj. typesafe and readable, but more verbose. easier to debug.\n        BlogListDTO retrievedBlogList = given()\n                .spec(spec)\n                .when()\n                .get(\"blogs\")\n                .then()\n                .statusCode(200)\n                .extract().as(BlogListDTO.class);\n        assertThat(retrievedBlogList.blogs)\n                .extracting(blogEntry -> blogEntry.id)\n                .contains(createdBlogId);\n        //nice extracting() (like map() from java 8 stream api)\n\n        //b) jsonpath + hamcrest. jsonpath is also powerful\n        // (recognizes that \"blogs\" is a field. \"blogs.id\" returns list of ids.\n        // less robust, harder to debug, but more concise. but trouble with types.\n        given()\n                .spec(spec)\n                .when()\n                .get(\"blogs\")\n                .then()\n                .statusCode(200)\n                .content(\"blogs.id\", hasItem(createdBlogId));\n        //however, jsonpath can be useful -> see docs for more details\n\n        //c) jsonpath + assertj\n        JsonPath retrievedBlogList2 = given()\n                .spec(spec)\n                .when()\n                .get(\"blogs\")\n                .then()\n                .statusCode(200)\n                .extract().jsonPath();\n        assertThat(retrievedBlogList2.getList(\"blogs.id\"))\n                .contains(createdBlogId);\n    }\n\n    @Test\n    public void jsonPath(){\n        JsonPath jsonPath = new JsonPath(\"{\\\"blogs\\\":[\\\"posts\\\":[{\\\"author\\\":{\\\"name\\\":\\\"Paul\\\"}}]]}\");\n        //jsonpath useful when accessing one element in a deeply nested document. don't write a pojo class if you only interested in one element\n        jsonPath.getString(\"blogs[0].posts[0].author.name\");\n    }\n\n    private int extractId(String resourceLocation) {\n        int slashIndex = resourceLocation.lastIndexOf(\"/\");\n        String idString = resourceLocation.substring(slashIndex + 1);\n        return Integer.parseInt(idString);\n    }\n\n    //Wait and Poll: Dealing with asynchronous behavior (like events)\n    //use awaitility to wait and poll until a certain assertion becomes true or a timeout exceeds.\n    //import static com.jayway.awaitility.Awaitility.await;\n    @Test\n    public void waitAndPoll(){\n        sendAsyncEventThatChangesABlog(123);\n        await().atMost(Duration.TWO_SECONDS).until(() -> {\n            given()\n                    .when()\n                    .get(\"blogs/123\")\n                    .then()\n                    .statusCode(200);\n        });\n    }\n\n    private void sendAsyncEventThatChangesABlog(int i) {\n    }\n\n    //await(), atMost() etc returns immutable ConditionFactory. -> configure once behavior for polling and waiting and reuse it\n    public static final ConditionFactory WAIT = await()\n            .atMost(new Duration(15, TimeUnit.SECONDS))\n            .pollInterval(Duration.ONE_SECOND)\n            .pollDelay(Duration.ONE_SECOND);\n    @Test\n    public void waitAndPoll2(){\n        WAIT.until(() -> {\n            //...\n        });\n    }\n\n\n        //    - Given-When-Then pattern\n    //    - keep this parts short. best: only one or a few parameterized method invocation. use submethods.\n    @Test\n    public void test(){\n        //Given: set up the input for the action under test (test data, mocks, stubs)\n        //When: execute the action you want to test.\n        //Then: check the output with assertions\n    }\n}\n"
  },
  {
    "path": "testingrestservice/integration-tests/src/test/java/de/philipphauer/blog/testingrestservice/integrationtests/dto/BlogDTO.java",
    "content": "package de.philipphauer.blog.testingrestservice.integrationtests.dto;\n\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\n\n@JsonIgnoreProperties(ignoreUnknown = true)\npublic class BlogDTO {\n\n    private String name;\n    private String description;\n    private String url;\n\n\n    public BlogDTO() {}\n\n    //don't:\n    public BlogDTO(String name, String description, String url) {\n        this.name = name;\n        this.description = description;\n        this.url = url;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public BlogDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public BlogDTO setDescription(String description) {\n        this.description = description;\n        return this;\n    }\n\n    public String getUrl() {\n        return url;\n    }\n\n    public BlogDTO setUrl(String url) {\n        this.url = url;\n        return this;\n    }\n}\n"
  },
  {
    "path": "testingrestservice/integration-tests/src/test/java/de/philipphauer/blog/testingrestservice/integrationtests/dto/BlogListDTO.java",
    "content": "package de.philipphauer.blog.testingrestservice.integrationtests.dto;\n\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\n\nimport java.util.List;\n\n@JsonIgnoreProperties(ignoreUnknown = true)\npublic class BlogListDTO {\n\n    //we leave out offset and limit, because we are not interested in this fields.\n    public int count;\n    public List<BlogReference> blogs;\n\n    public static class BlogReference{\n        public int id;\n        public String name;\n        public String href;\n    }\n}\n"
  },
  {
    "path": "testingrestservice/integration-tests/src/test/java/de/philipphauer/blog/testingrestservice/integrationtests/dtoKotlin/BlogDTOKotlin.kt",
    "content": "package de.philipphauer.blog.testingrestservice.integrationtests.dtoKotlin\n\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties\n\n//definition:\ndata class BlogDTO (val name: String, val description: String, val url: String)\n//getters, constructor, hashCode(), equals(), toString(), copy() are included!\n\n//usage:\nval newBlog = BlogDTO(\n        name = \"Example\",\n        description = \"Example\",\n        url = \"www.blogdomain.de\")\n//readable due to named arguments\n\n//let's use different DTOs for creation and retrieval, because they differ (id!)\n@JsonIgnoreProperties(ignoreUnknown = true)\ndata class BlogRetrievalDTO (val name: String, val description: String, val url: String)"
  },
  {
    "path": "testingrestservice/integration-tests/src/test/java/de/philipphauer/blog/testingrestservice/integrationtests/dtoKotlin/BlogsKotlinTest.kt",
    "content": "package de.philipphauer.blog.testingrestservice.integrationtests.dtoKotlin\n\nimport com.jayway.restassured.RestAssured.given\nimport com.jayway.restassured.builder.RequestSpecBuilder\nimport com.jayway.restassured.filter.log.RequestLoggingFilter\nimport com.jayway.restassured.filter.log.ResponseLoggingFilter\nimport com.jayway.restassured.http.ContentType\nimport com.jayway.restassured.specification.RequestSpecification\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.BeforeClass\nimport org.junit.Test\n\nclass BlogsKotlinTest {\n\n    companion object{\n        private var spec: RequestSpecification? = null\n\n        @JvmStatic\n        @BeforeClass\n        fun initSpec() {\n            spec = RequestSpecBuilder()\n                    .setContentType(ContentType.JSON)\n                    .setBaseUri(\"http://localhost:8080/\")\n                    .addFilter(ResponseLoggingFilter())\n                    .addFilter(RequestLoggingFilter())\n                    .build()\n        }\n    }\n\n    @Test\n    fun kotlinPower(){\n        //it's important to add the jackson-module-kotlin to your project dependencies\n        //in order to make the deserialization to the Kotlin object work.\n        val retrievedBlog = given()\n                .spec(spec)\n                .`when`()\n                .get(\"blogs/1\")\n                .then()\n                .statusCode(200)\n                .extract().`as`(BlogRetrievalDTO::class.java)\n        assertThat(retrievedBlog.name).isNotEmpty()\n    }\n}"
  },
  {
    "path": "testingrestservice/service/.gitignore",
    "content": "*.iml\n.idea\ntarget\ndb"
  },
  {
    "path": "testingrestservice/service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.3.2.RELEASE</version>\n    </parent>\n    <groupId>de.philipphauer.blog.testingrestservice</groupId>\n    <artifactId>service</artifactId>\n    <version>1.0-SNAPSHOT</version>\n\n    <properties>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-jpa</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.h2database</groupId>\n            <artifactId>h2</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-devtools</artifactId>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>de.sven-jacobs</groupId>\n            <artifactId>loremipsum</artifactId>\n            <version>1.0</version>\n        </dependency>\n        <!-- mapping for java 8 time objects -->\n        <dependency>\n            <groupId>com.fasterxml.jackson.datatype</groupId>\n            <artifactId>jackson-datatype-jsr310</artifactId>\n            <version>2.7.0</version>\n        </dependency>\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <version>4.12</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.squareup.okhttp3</groupId>\n            <artifactId>mockwebserver</artifactId>\n            <version>3.2.0</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.assertj</groupId>\n            <artifactId>assertj-core</artifactId>\n            <version>1.7.1</version>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>"
  },
  {
    "path": "testingrestservice/service/src/main/java/de/philipphauer/blog/testingrestservice/service/BlogApplication.java",
    "content": "package de.philipphauer.blog.testingrestservice.service;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class BlogApplication {\n\n    private static final Logger log = LoggerFactory.getLogger(BlogApplication.class);\n\n    public static void main(String[] args) {\n        SpringApplication.run(BlogApplication.class);\n        log.info(\"/h2-console with url='jdbc:h2:mem:testdb;', user='sa', pw=''\");\n    }\n}"
  },
  {
    "path": "testingrestservice/service/src/main/java/de/philipphauer/blog/testingrestservice/service/dataaccess/BlogRepository.java",
    "content": "package de.philipphauer.blog.testingrestservice.service.dataaccess;\n\nimport de.philipphauer.blog.testingrestservice.service.dataaccess.entities.BlogEntity;\nimport org.springframework.data.repository.CrudRepository;\n\npublic interface BlogRepository extends CrudRepository<BlogEntity, Long> {\n\n}"
  },
  {
    "path": "testingrestservice/service/src/main/java/de/philipphauer/blog/testingrestservice/service/dataaccess/DatabaseInitializer.java",
    "content": "package de.philipphauer.blog.testingrestservice.service.dataaccess;\n\nimport de.philipphauer.blog.testingrestservice.service.dataaccess.entities.BlogEntity;\nimport de.philipphauer.blog.testingrestservice.service.dataaccess.entities.CommentEntity;\nimport de.philipphauer.blog.testingrestservice.service.dataaccess.entities.PostEntity;\nimport de.svenjacobs.loremipsum.LoremIpsum;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.CommandLineRunner;\nimport org.springframework.stereotype.Component;\n\nimport java.time.LocalDateTime;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Random;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n@Component\npublic class DatabaseInitializer implements CommandLineRunner {\n\n    private static final Logger log = LoggerFactory.getLogger(DatabaseInitializer.class);\n    LoremIpsum loremIpsum = new LoremIpsum();\n\n    @Autowired\n    private BlogRepository repo;\n\n    @Override\n    public void run(String... strings) throws Exception {\n        log.info(\"Creating dummy data.\");\n\n        List<BlogEntity> blogs = createBlogs(6);\n        repo.save(blogs);\n    }\n\n    private List<CommentEntity> createComments(int amount) {\n        return Stream.generate(() -> new CommentEntity()\n                .setAuthor(createRandomName())\n                .setCreatedDateTime(LocalDateTime.now())\n                .setContent(createRandomCommentText())\n        )\n                .limit(amount)\n                .collect(Collectors.toList());\n    }\n\n    private List<PostEntity> createPosts(int amount) {\n        return Stream.generate(() -> {\n            String title = createRandomPostTitle();\n            String slug = toSlug(title);\n            return new PostEntity()\n                .setAuthor(createRandomName())\n                .setCreatedDateTime(LocalDateTime.now())\n                .setTeaser(loremIpsum.getParagraphs(1))\n                .setContent(loremIpsum.getParagraphs(7))\n                .setComments(createComments(5))\n                .setTitle(title)\n                .setSlug(slug)\n                .setViewCount(random.nextInt(1000))\n                .setTags(createRandomTags())\n                .setFeaturedImage(slug+\".png\");\n            }\n            )\n            .limit(amount)\n            .collect(Collectors.toList());\n    }\n\n    private List<BlogEntity> createBlogs(int amount) {\n        return Stream.generate(() -> {\n            String title = createRandomBlogTitle();\n            return new BlogEntity()\n                .setName(title)\n                .setDescription(loremIpsum.getParagraphs(1))\n                .setPosts(createPosts(9))\n                .setUrl(\"http://www.\" +toSlug(title) + \".com\");\n            }\n        )\n            .limit(amount)\n            .collect(Collectors.toList());\n    }\n\n    private Random random = new Random();\n\n    private List<String> firstNames = Arrays.asList(\"Max\", \"Paul\", \"Tim\", \"Nils\", \"Angela\", \"Maria\", \"Lea\", \"Sven\", \"Helena\");\n    private List<String> lastNames = Arrays.asList(\"Müller\", \"Schmidt\", \"Merkel\", \"Henkel\", \"Lange\", \"Marx\", \"Heine\", \"Fischer\", \"Bauer\");\n\n    private String createRandomName() {\n        String firstName = getRandomElement(firstNames);\n        String lastName = getRandomElement(lastNames);\n        return firstName + \" \" + lastName;\n    }\n\n    private List<String> comments = Arrays.asList(\"Cool!\", \"Awesome!\", \"Thanks!\", \"Well done!\", \"That's terrible.\", \"I like nuts.\");\n\n    private String createRandomCommentText() {\n        return getRandomElement(comments);\n    }\n\n    private List<String> parts1 = Arrays.asList(\"Creating\", \"Analysing\", \"Designing\", \"Implementing\", \"Investigating\", \"Ignoring\");\n    private List<String> parts2 = Arrays.asList(\"Performance\", \"Footprint\", \"Code\", \"Quality\", \"Architecture\", \"Speed\", \"Test Code\", \"Communication\");\n    private List<String> parts3 = Arrays.asList(\"of Microservices\", \"of Docker\", \"of Spring Boot\", \"of a Vaadin application\", \"of RESTful Services\", \"of SOAP Services\", \"of angular.js\", \"of react.js\");\n\n    private String createRandomPostTitle() {\n        String part1 = getRandomElement(parts1);\n        String part2 = getRandomElement(parts2);\n        String part3 = getRandomElement(parts3);\n        return part1 + \" \" + part2 + \" \" + part3;\n    }\n\n    private List<String> blogTitles = Arrays.asList(\"Java Ecosystem\", \"Web Development\", \"Web Architecture\", \"Software Architecture\", \"Software Archaeology\", \"Test Driven Development\", \"Model Driven Development\", \"Software Craftsmanship\", \"Build and Delivery\");\n\n    private String createRandomBlogTitle() {\n        return getRandomElement(blogTitles);\n    }\n\n    private String getRandomElement(List<String> list) {\n        return list.get(random.nextInt(list.size()));\n    }\n\n    private List<String> tags = Arrays.asList(\"Java\", \"Web\", \"Build\", \"Architecture\", \"Development\", \"Integration\", \"Modelling\", \"REST\", \"Scalability\", \"Cloud\", \"SOAP\", \"HTTP\", \"Swagger\", \"Best Practice\", \"Test\", \"TDD\", \"Vaadin\", \"Clean Code\", \"HATEOAS\", \"Spring Boot\");\n\n    private String[] createRandomTags() {\n        return new String[]{getRandomElement(tags), getRandomElement(tags), getRandomElement(tags)};\n//        return Arrays.asList(getRandomElement(tags), getRandomElement(tags), getRandomElement(tags));\n    }\n\n    public String toSlug(String blogTitle){\n        return blogTitle.replaceAll(\" \", \"_\").toLowerCase();\n    }\n}\n\n"
  },
  {
    "path": "testingrestservice/service/src/main/java/de/philipphauer/blog/testingrestservice/service/dataaccess/PostRepository.java",
    "content": "package de.philipphauer.blog.testingrestservice.service.dataaccess;\n\nimport de.philipphauer.blog.testingrestservice.service.dataaccess.entities.PostEntity;\nimport org.springframework.data.repository.CrudRepository;\n\npublic interface PostRepository extends CrudRepository<PostEntity, Long> {\n\n}\n"
  },
  {
    "path": "testingrestservice/service/src/main/java/de/philipphauer/blog/testingrestservice/service/dataaccess/entities/BlogEntity.java",
    "content": "package de.philipphauer.blog.testingrestservice.service.dataaccess.entities;\n\nimport javax.persistence.*;\nimport javax.validation.constraints.Size;\nimport java.util.List;\n\n@Entity\npublic class BlogEntity {\n\n    @Id\n    @GeneratedValue(strategy= GenerationType.AUTO)\n    private long id;\n    private String name;\n    @Size(max=300)\n    private String description;\n    private String url;\n    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)\n    private List<PostEntity> posts;\n\n    public long getId() {\n        return id;\n    }\n\n    public BlogEntity setId(long id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public BlogEntity setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public BlogEntity setDescription(String description) {\n        this.description = description;\n        return this;\n    }\n\n    public List<PostEntity> getPosts() {\n        return posts;\n    }\n\n    public BlogEntity setPosts(List<PostEntity> posts) {\n        this.posts = posts;\n        return this;\n    }\n\n    public String getUrl() {\n        return url;\n    }\n\n    public BlogEntity setUrl(String url) {\n        this.url = url;\n        return this;\n    }\n}"
  },
  {
    "path": "testingrestservice/service/src/main/java/de/philipphauer/blog/testingrestservice/service/dataaccess/entities/CommentEntity.java",
    "content": "package de.philipphauer.blog.testingrestservice.service.dataaccess.entities;\n\nimport javax.persistence.*;\nimport java.time.LocalDateTime;\n\n@Entity\npublic class CommentEntity {\n\n    @Id\n    @GeneratedValue(strategy= GenerationType.AUTO)\n    private long id;\n    private LocalDateTime createdDateTime;\n    private String author;\n    @Lob\n    private String content;\n\n    public long getId() {\n        return id;\n    }\n\n    public CommentEntity setId(long id) {\n        this.id = id;\n        return this;\n    }\n\n    public LocalDateTime getCreatedDateTime() {\n        return createdDateTime;\n    }\n\n    public CommentEntity setCreatedDateTime(LocalDateTime createdDateTime) {\n        this.createdDateTime = createdDateTime;\n        return this;\n    }\n\n    public String getAuthor() {\n        return author;\n    }\n\n    public CommentEntity setAuthor(String author) {\n        this.author = author;\n        return this;\n    }\n\n    public String getContent() {\n        return content;\n    }\n\n    public CommentEntity setContent(String content) {\n        this.content = content;\n        return this;\n    }\n}\n"
  },
  {
    "path": "testingrestservice/service/src/main/java/de/philipphauer/blog/testingrestservice/service/dataaccess/entities/PostEntity.java",
    "content": "package de.philipphauer.blog.testingrestservice.service.dataaccess.entities;\n\nimport javax.persistence.*;\nimport javax.validation.constraints.Size;\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Entity\npublic class PostEntity {\n\n    @Id\n    @GeneratedValue(strategy= GenerationType.AUTO)\n    private long id;\n    private LocalDateTime createdDateTime;\n    private String title;\n    private String author;\n    private String slug;\n    private int viewCount;\n    private String featuredImage;\n    private String[] tags;\n    @Size(max=300)\n    private String teaser;\n    @Lob\n    private String content;\n    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)\n    private List<CommentEntity> comments;\n\n    public String[] getTags() {\n        return tags;\n    }\n\n    public PostEntity setTags(String[] tags) {\n        this.tags = tags;\n        return this;\n    }\n\n    public String getFeaturedImage() {\n        return featuredImage;\n    }\n\n    public PostEntity setFeaturedImage(String featuredImage) {\n        this.featuredImage = featuredImage;\n        return this;\n    }\n\n    public int getViewCount() {\n        return viewCount;\n    }\n\n    public PostEntity setViewCount(int viewCount) {\n        this.viewCount = viewCount;\n        return this;\n    }\n\n    public String getSlug() {\n        return slug;\n    }\n\n    public PostEntity setSlug(String slug) {\n        this.slug = slug;\n        return this;\n    }\n\n    public long getId() {\n        return id;\n    }\n\n    public PostEntity setId(long id) {\n        this.id = id;\n        return this;\n    }\n\n    public LocalDateTime getCreatedDateTime() {\n        return createdDateTime;\n    }\n\n    public PostEntity setCreatedDateTime(LocalDateTime createdDateTime) {\n        this.createdDateTime = createdDateTime;\n        return this;\n    }\n\n    public String getTitle() {\n        return title;\n    }\n\n    public PostEntity setTitle(String title) {\n        this.title = title;\n        return this;\n    }\n\n    public String getTeaser() {\n        return teaser;\n    }\n\n    public PostEntity setTeaser(String teaser) {\n        this.teaser = teaser;\n        return this;\n    }\n\n    public String getContent() {\n        return content;\n    }\n\n    public PostEntity setContent(String content) {\n        this.content = content;\n        return this;\n    }\n\n    public List<CommentEntity> getComments() {\n        return comments;\n    }\n\n    public PostEntity setComments(List<CommentEntity> comments) {\n        this.comments = comments;\n        return this;\n    }\n\n    public String getAuthor() {\n        return author;\n    }\n\n    public PostEntity setAuthor(String author) {\n        this.author = author;\n        return this;\n    }\n}\n"
  },
  {
    "path": "testingrestservice/service/src/main/java/de/philipphauer/blog/testingrestservice/service/rest/BlogsResource.java",
    "content": "package de.philipphauer.blog.testingrestservice.service.rest;\n\nimport de.philipphauer.blog.testingrestservice.service.dataaccess.BlogRepository;\nimport de.philipphauer.blog.testingrestservice.service.dataaccess.PostRepository;\nimport de.philipphauer.blog.testingrestservice.service.dataaccess.entities.BlogEntity;\nimport de.philipphauer.blog.testingrestservice.service.dataaccess.entities.PostEntity;\nimport de.philipphauer.blog.testingrestservice.service.rest.dto.BlogDTO;\nimport de.philipphauer.blog.testingrestservice.service.rest.dto.BlogsDTO;\nimport de.philipphauer.blog.testingrestservice.service.rest.dto.ReferenceDTO;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.web.util.UriComponents;\nimport org.springframework.web.util.UriComponentsBuilder;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n@RestController\n@RequestMapping(value = \"/blogs\", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE})\npublic class BlogsResource {\n\n    @Autowired\n    private BlogRepository blogRepo;\n    @Autowired\n    private PostRepository postRepo;\n\n    @RequestMapping(value = \"\", method = RequestMethod.POST)\n    public ResponseEntity createBlog(UriComponentsBuilder uriBuilder, @RequestBody BlogDTO blogDto) {\n        BlogEntity blog = mapToEntry(blogDto);\n        blogRepo.save(blog);\n\n        UriComponents uriComponents =\n                uriBuilder.path(\"blogs/{id}\").buildAndExpand(blog.getId());\n        HttpHeaders headers = new HttpHeaders();\n        headers.setLocation(uriComponents.toUri());\n        return new ResponseEntity<Void>(headers, HttpStatus.CREATED);\n    }\n\n    private BlogEntity mapToEntry(BlogDTO blogDto) {\n        return new BlogEntity()\n                .setUrl(blogDto.getUrl())\n                .setDescription(blogDto.getDescription())\n                .setName(blogDto.getName());\n    }\n\n    @RequestMapping(value = \"\", method = RequestMethod.GET)\n    public BlogsDTO getAll() {\n        Collection<BlogEntity> blogs = (Collection<BlogEntity>) blogRepo.findAll();\n        BlogsDTO blogList = mapToDTO(blogs);\n        return blogList;\n    }\n\n    @RequestMapping(value = \"/{id}\", method = RequestMethod.GET)\n    public BlogDTO getBlog(@PathVariable(\"id\") Long id) {\n        BlogEntity blog = blogRepo.findOne(id);\n        BlogDTO blogDTO = mapToDTO(blog);\n        return blogDTO;\n    }\n\n    @RequestMapping(value = \"/{blogId}/posts\", method = RequestMethod.GET)\n    public List<ReferenceDTO> getAllBlogPosts(@PathVariable(\"blogId\") Long blogId) {\n        BlogEntity blog = blogRepo.findOne(blogId);\n        List<PostEntity> posts = blog.getPosts();\n        return mapToDTO(blog.getId(), posts);\n    }\n\n    //let's ignore the blogId for the sake of simplicity, because the postId is unique and and not context is necessary\n    @RequestMapping(value = \"/{blogId}/posts/{postId}\", method = RequestMethod.GET)\n    public PostEntity getBlogPost(@PathVariable(\"blogId\") Long blogId, @PathVariable(\"postId\") Long postId) {\n        PostEntity post = postRepo.findOne(postId);\n        return post;\n    }\n\n    private BlogDTO mapToDTO(BlogEntity blog) {\n        List<PostEntity> posts = blog.getPosts();\n        List<ReferenceDTO> postsDTO = mapToDTO(blog.getId(), posts);\n        BlogDTO blogDTO = new BlogDTO()\n                .setName(blog.getName())\n                .setDescription(blog.getDescription())\n                .setPosts(postsDTO)\n                .setUrl(blog.getUrl());\n        return blogDTO;\n    }\n\n    private List<ReferenceDTO> mapToDTO(long blogId, List<PostEntity> posts) {\n        return posts.stream()\n                    .map(post -> mapToDTO(blogId, post))\n                    .collect(Collectors.toList());\n    }\n\n    private ReferenceDTO mapToDTO(long blogId, PostEntity post){\n        long id = post.getId();\n        String title = post.getTitle();\n        String href = \"/blogs/\" + blogId + \"/posts/\" + id;\n        ReferenceDTO ref = new ReferenceDTO().setId(id).setName(title).setHref(href);\n        return ref;\n    }\n\n    private BlogsDTO mapToDTO(Collection<BlogEntity> blogs) {\n        List<ReferenceDTO> blogRefs = blogs.stream()\n                .map(blogEntry -> {\n                    String name = blogEntry.getName();\n                    long id = blogEntry.getId();\n                    String href = \"/blogs/\" + id;\n                    ReferenceDTO ref = new ReferenceDTO().setId(id).setName(name).setHref(href);\n                    return ref;\n                })\n                .collect(Collectors.toList());\n        return new BlogsDTO()\n                .setBlogs(blogRefs)\n                .setOffset(0)\n                .setLimit(50)\n                .setCount(blogs.size());\n    }\n}\n"
  },
  {
    "path": "testingrestservice/service/src/main/java/de/philipphauer/blog/testingrestservice/service/rest/dto/BlogDTO.java",
    "content": "package de.philipphauer.blog.testingrestservice.service.rest.dto;\n\nimport java.util.List;\n\npublic class BlogDTO {\n\n    private String name;\n    private String description;\n    private String url;\n    private List<ReferenceDTO> posts;\n\n    public String getUrl() {\n        return url;\n    }\n\n    public BlogDTO setUrl(String url) {\n        this.url = url;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public BlogDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public BlogDTO setDescription(String description) {\n        this.description = description;\n        return this;\n    }\n\n    public List<ReferenceDTO> getPosts() {\n        return posts;\n    }\n\n    public BlogDTO setPosts(List<ReferenceDTO> posts) {\n        this.posts = posts;\n        return this;\n    }\n}\n"
  },
  {
    "path": "testingrestservice/service/src/main/java/de/philipphauer/blog/testingrestservice/service/rest/dto/BlogsDTO.java",
    "content": "package de.philipphauer.blog.testingrestservice.service.rest.dto;\n\nimport java.util.List;\n\npublic class BlogsDTO {\n\n    private int count;\n    private int offset;\n    private int limit;\n    private List<ReferenceDTO> blogs;\n\n    public List<ReferenceDTO> getBlogs() {\n        return blogs;\n    }\n\n    public int getCount() {\n        return count;\n    }\n\n    public int getOffset() {\n        return offset;\n    }\n\n    public int getLimit() {\n        return limit;\n    }\n\n    public BlogsDTO setBlogs(List<ReferenceDTO> blogs) {\n        this.blogs = blogs;\n        return this;\n    }\n\n    public BlogsDTO setCount(int count) {\n        this.count = count;\n        return this;\n    }\n\n    public BlogsDTO setOffset(int offset) {\n        this.offset = offset;\n        return this;\n    }\n\n    public BlogsDTO setLimit(int limit) {\n        this.limit = limit;\n        return this;\n    }\n}\n"
  },
  {
    "path": "testingrestservice/service/src/main/java/de/philipphauer/blog/testingrestservice/service/rest/dto/ReferenceDTO.java",
    "content": "package de.philipphauer.blog.testingrestservice.service.rest.dto;\n\npublic class ReferenceDTO {\n\n    private long id;\n    private String name;\n    private String href;\n\n    public String getName() {\n        return name;\n    }\n\n    public ReferenceDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public long getId() {\n        return id;\n    }\n\n    public ReferenceDTO setId(long id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getHref() {\n        return href;\n    }\n\n    public ReferenceDTO setHref(String href) {\n        this.href = href;\n        return this;\n    }\n}\n"
  },
  {
    "path": "testingrestservice/service/src/main/java/de/philipphauer/blog/testingrestservice/service/servicecall/ImageReference.java",
    "content": "package de.philipphauer.blog.testingrestservice.service.servicecall;\n\npublic class ImageReference {\n\n    private String id;\n    private String href;\n\n    public String getId() {\n        return id;\n    }\n\n    public ImageReference setId(String id) {\n        this.id = id;\n        return this;\n    }\n\n\n    public String getHref() {\n        return href;\n    }\n\n    public ImageReference setHref(String href) {\n        this.href = href;\n        return this;\n    }\n}\n"
  },
  {
    "path": "testingrestservice/service/src/main/java/de/philipphauer/blog/testingrestservice/service/servicecall/ImageServiceClient.java",
    "content": "package de.philipphauer.blog.testingrestservice.service.servicecall;\n\nimport org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;\nimport org.springframework.web.client.RestTemplate;\n\nimport java.util.Arrays;\n\npublic class ImageServiceClient {\n\n    private final String imageServiceHost;\n    private final int imageServicePort;\n\n    public ImageServiceClient(String imageServiceHost, int imageServicePort) {\n        this.imageServiceHost = imageServiceHost;\n        this.imageServicePort = imageServicePort;\n    }\n\n    public ImageReference requestImage(String id){\n        RestTemplate restTemplate = new RestTemplate(Arrays.asList(new MappingJackson2HttpMessageConverter()));\n        ImageReference imageReference = restTemplate.getForObject(\"http://{host}:{port}/images/{id}\",\n                ImageReference.class, imageServiceHost, imageServicePort, id);\n        return imageReference;\n    }\n}\n"
  },
  {
    "path": "testingrestservice/service/src/main/resources/application.properties",
    "content": "spring.jackson.serialization.write_dates_as_timestamps=false\nlogging.level.org.springframework.web=INFO\nlogging.level.org.hibernate=INFO"
  },
  {
    "path": "testingrestservice/service/src/test/java/de/philipphauer/blog/testingrestservice/service/servicecall/ImageReferenceServiceClientTest.java",
    "content": "package de.philipphauer.blog.testingrestservice.service.servicecall;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport okhttp3.HttpUrl;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.IOException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class ImageReferenceServiceClientTest {\n\n    private MockWebServer imageService;\n    private ImageServiceClient imageClient;\n\n    @Before\n    public void init() throws IOException {\n        imageService = new MockWebServer();\n        imageService.start(); //search for an available port\n        HttpUrl baseUrl = imageService.url(\"/images/\");\n        imageClient = new ImageServiceClient(baseUrl.host(), baseUrl.port());\n    }\n\n    @Test\n    public void requestImage() throws JsonProcessingException {\n        ImageReference expectedImageRef = new ImageReference().setId(\"123\").setHref(\"http://images.company.org/123\");\n        String json = new ObjectMapper().writeValueAsString(expectedImageRef);\n        imageService.enqueue(new MockResponse()\n                .addHeader(\"Content-Type\", \"application/json\")\n                .setBody(json));\n\n        ImageReference retrievedImageRef = imageClient.requestImage(\"123\");\n\n        assertThat(retrievedImageRef.getId()).isEqualTo(expectedImageRef.getId());\n        assertThat(retrievedImageRef.getHref()).isEqualTo(expectedImageRef.getHref());\n    }\n}"
  },
  {
    "path": "ti-continuation-token/.gitignore",
    "content": "target/\n!.mvn/wrapper/maven-wrapper.jar\n.idea/\n*.iml\ndependency-reduced-pom.xml"
  },
  {
    "path": "ti-continuation-token/.mvn/wrapper/maven-wrapper.properties",
    "content": "distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.2/apache-maven-3.5.2-bin.zip\n"
  },
  {
    "path": "ti-continuation-token/README.md",
    "content": "# Example Implementation for the `Timestamp_ID` Continuation Token\n\n```bash\n./mvnw package && java -jar target/demo-kotlin*.jar\n```\n\nOpen `http://localhost:8000/designs?pageSize=3` in your browser and click on the URL in the `nextPage` field in the json payload. You can also submit a certain start date with the query parameter `modifiedSince`: `http://localhost:8000/designs?pageSize=3&modifiedSince=1512757072`\n\nThe demo application is a lightweight HTTP service written in Kotlin and powered by [HTTP4K](https://www.http4k.org/). It starts within 600 ms 🏇\n"
  },
  {
    "path": "ti-continuation-token/mvnw",
    "content": "#!/bin/sh\n# ----------------------------------------------------------------------------\n# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\n# ----------------------------------------------------------------------------\n\n# ----------------------------------------------------------------------------\n# Maven2 Start Up Batch script\n#\n# Required ENV vars:\n# ------------------\n#   JAVA_HOME - location of a JDK home dir\n#\n# Optional ENV vars\n# -----------------\n#   M2_HOME - location of maven2's installed home dir\n#   MAVEN_OPTS - parameters passed to the Java VM when running Maven\n#     e.g. to debug Maven itself, use\n#       set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000\n#   MAVEN_SKIP_RC - flag to disable loading of mavenrc files\n# ----------------------------------------------------------------------------\n\nif [ -z \"$MAVEN_SKIP_RC\" ] ; then\n\n  if [ -f /etc/mavenrc ] ; then\n    . /etc/mavenrc\n  fi\n\n  if [ -f \"$HOME/.mavenrc\" ] ; then\n    . \"$HOME/.mavenrc\"\n  fi\n\nfi\n\n# OS specific support.  $var _must_ be set to either true or false.\ncygwin=false;\ndarwin=false;\nmingw=false\ncase \"`uname`\" in\n  CYGWIN*) cygwin=true ;;\n  MINGW*) mingw=true;;\n  Darwin*) darwin=true\n    # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home\n    # See https://developer.apple.com/library/mac/qa/qa1170/_index.html\n    if [ -z \"$JAVA_HOME\" ]; then\n      if [ -x \"/usr/libexec/java_home\" ]; then\n        export JAVA_HOME=\"`/usr/libexec/java_home`\"\n      else\n        export JAVA_HOME=\"/Library/Java/Home\"\n      fi\n    fi\n    ;;\nesac\n\nif [ -z \"$JAVA_HOME\" ] ; then\n  if [ -r /etc/gentoo-release ] ; then\n    JAVA_HOME=`java-config --jre-home`\n  fi\nfi\n\nif [ -z \"$M2_HOME\" ] ; then\n  ## resolve links - $0 may be a link to maven's home\n  PRG=\"$0\"\n\n  # need this for relative symlinks\n  while [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n      PRG=\"$link\"\n    else\n      PRG=\"`dirname \"$PRG\"`/$link\"\n    fi\n  done\n\n  saveddir=`pwd`\n\n  M2_HOME=`dirname \"$PRG\"`/..\n\n  # make it fully qualified\n  M2_HOME=`cd \"$M2_HOME\" && pwd`\n\n  cd \"$saveddir\"\n  # echo Using m2 at $M2_HOME\nfi\n\n# For Cygwin, ensure paths are in UNIX format before anything is touched\nif $cygwin ; then\n  [ -n \"$M2_HOME\" ] &&\n    M2_HOME=`cygpath --unix \"$M2_HOME\"`\n  [ -n \"$JAVA_HOME\" ] &&\n    JAVA_HOME=`cygpath --unix \"$JAVA_HOME\"`\n  [ -n \"$CLASSPATH\" ] &&\n    CLASSPATH=`cygpath --path --unix \"$CLASSPATH\"`\nfi\n\n# For Migwn, ensure paths are in UNIX format before anything is touched\nif $mingw ; then\n  [ -n \"$M2_HOME\" ] &&\n    M2_HOME=\"`(cd \"$M2_HOME\"; pwd)`\"\n  [ -n \"$JAVA_HOME\" ] &&\n    JAVA_HOME=\"`(cd \"$JAVA_HOME\"; pwd)`\"\n  # TODO classpath?\nfi\n\nif [ -z \"$JAVA_HOME\" ]; then\n  javaExecutable=\"`which javac`\"\n  if [ -n \"$javaExecutable\" ] && ! [ \"`expr \\\"$javaExecutable\\\" : '\\([^ ]*\\)'`\" = \"no\" ]; then\n    # readlink(1) is not available as standard on Solaris 10.\n    readLink=`which readlink`\n    if [ ! `expr \"$readLink\" : '\\([^ ]*\\)'` = \"no\" ]; then\n      if $darwin ; then\n        javaHome=\"`dirname \\\"$javaExecutable\\\"`\"\n        javaExecutable=\"`cd \\\"$javaHome\\\" && pwd -P`/javac\"\n      else\n        javaExecutable=\"`readlink -f \\\"$javaExecutable\\\"`\"\n      fi\n      javaHome=\"`dirname \\\"$javaExecutable\\\"`\"\n      javaHome=`expr \"$javaHome\" : '\\(.*\\)/bin'`\n      JAVA_HOME=\"$javaHome\"\n      export JAVA_HOME\n    fi\n  fi\nfi\n\nif [ -z \"$JAVACMD\" ] ; then\n  if [ -n \"$JAVA_HOME\"  ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n      # IBM's JDK on AIX uses strange locations for the executables\n      JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n      JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n  else\n    JAVACMD=\"`which java`\"\n  fi\nfi\n\nif [ ! -x \"$JAVACMD\" ] ; then\n  echo \"Error: JAVA_HOME is not defined correctly.\" >&2\n  echo \"  We cannot execute $JAVACMD\" >&2\n  exit 1\nfi\n\nif [ -z \"$JAVA_HOME\" ] ; then\n  echo \"Warning: JAVA_HOME environment variable is not set.\"\nfi\n\nCLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher\n\n# traverses directory structure from process work directory to filesystem root\n# first directory with .mvn subdirectory is considered project base directory\nfind_maven_basedir() {\n\n  if [ -z \"$1\" ]\n  then\n    echo \"Path not specified to find_maven_basedir\"\n    return 1\n  fi\n\n  basedir=\"$1\"\n  wdir=\"$1\"\n  while [ \"$wdir\" != '/' ] ; do\n    if [ -d \"$wdir\"/.mvn ] ; then\n      basedir=$wdir\n      break\n    fi\n    # workaround for JBEAP-8937 (on Solaris 10/Sparc)\n    if [ -d \"${wdir}\" ]; then\n      wdir=`cd \"$wdir/..\"; pwd`\n    fi\n    # end of workaround\n  done\n  echo \"${basedir}\"\n}\n\n# concatenates all lines of a file\nconcat_lines() {\n  if [ -f \"$1\" ]; then\n    echo \"$(tr -s '\\n' ' ' < \"$1\")\"\n  fi\n}\n\nBASE_DIR=`find_maven_basedir \"$(pwd)\"`\nif [ -z \"$BASE_DIR\" ]; then\n  exit 1;\nfi\n\nexport MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-\"$BASE_DIR\"}\necho $MAVEN_PROJECTBASEDIR\nMAVEN_OPTS=\"$(concat_lines \"$MAVEN_PROJECTBASEDIR/.mvn/jvm.config\") $MAVEN_OPTS\"\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin; then\n  [ -n \"$M2_HOME\" ] &&\n    M2_HOME=`cygpath --path --windows \"$M2_HOME\"`\n  [ -n \"$JAVA_HOME\" ] &&\n    JAVA_HOME=`cygpath --path --windows \"$JAVA_HOME\"`\n  [ -n \"$CLASSPATH\" ] &&\n    CLASSPATH=`cygpath --path --windows \"$CLASSPATH\"`\n  [ -n \"$MAVEN_PROJECTBASEDIR\" ] &&\n    MAVEN_PROJECTBASEDIR=`cygpath --path --windows \"$MAVEN_PROJECTBASEDIR\"`\nfi\n\nWRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain\n\nexec \"$JAVACMD\" \\\n  $MAVEN_OPTS \\\n  -classpath \"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar\" \\\n  \"-Dmaven.home=${M2_HOME}\" \"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}\" \\\n  ${WRAPPER_LAUNCHER} $MAVEN_CONFIG \"$@\"\n"
  },
  {
    "path": "ti-continuation-token/mvnw.cmd",
    "content": "@REM ----------------------------------------------------------------------------\n@REM Licensed to the Apache Software Foundation (ASF) under one\n@REM or more contributor license agreements.  See the NOTICE file\n@REM distributed with this work for additional information\n@REM regarding copyright ownership.  The ASF licenses this file\n@REM to you under the Apache License, Version 2.0 (the\n@REM \"License\"); you may not use this file except in compliance\n@REM with the License.  You may obtain a copy of the License at\n@REM\n@REM    http://www.apache.org/licenses/LICENSE-2.0\n@REM\n@REM Unless required by applicable law or agreed to in writing,\n@REM software distributed under the License is distributed on an\n@REM \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n@REM KIND, either express or implied.  See the License for the\n@REM specific language governing permissions and limitations\n@REM under the License.\n@REM ----------------------------------------------------------------------------\n\n@REM ----------------------------------------------------------------------------\n@REM Maven2 Start Up Batch script\n@REM\n@REM Required ENV vars:\n@REM JAVA_HOME - location of a JDK home dir\n@REM\n@REM Optional ENV vars\n@REM M2_HOME - location of maven2's installed home dir\n@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands\n@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending\n@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven\n@REM     e.g. to debug Maven itself, use\n@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000\n@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files\n@REM ----------------------------------------------------------------------------\n\n@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'\n@echo off\n@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'\n@if \"%MAVEN_BATCH_ECHO%\" == \"on\"  echo %MAVEN_BATCH_ECHO%\n\n@REM set %HOME% to equivalent of $HOME\nif \"%HOME%\" == \"\" (set \"HOME=%HOMEDRIVE%%HOMEPATH%\")\n\n@REM Execute a user defined script before this one\nif not \"%MAVEN_SKIP_RC%\" == \"\" goto skipRcPre\n@REM check for pre script, once with legacy .bat ending and once with .cmd ending\nif exist \"%HOME%\\mavenrc_pre.bat\" call \"%HOME%\\mavenrc_pre.bat\"\nif exist \"%HOME%\\mavenrc_pre.cmd\" call \"%HOME%\\mavenrc_pre.cmd\"\n:skipRcPre\n\n@setlocal\n\nset ERROR_CODE=0\n\n@REM To isolate internal variables from possible post scripts, we use another setlocal\n@setlocal\n\n@REM ==== START VALIDATION ====\nif not \"%JAVA_HOME%\" == \"\" goto OkJHome\n\necho.\necho Error: JAVA_HOME not found in your environment. >&2\necho Please set the JAVA_HOME variable in your environment to match the >&2\necho location of your Java installation. >&2\necho.\ngoto error\n\n:OkJHome\nif exist \"%JAVA_HOME%\\bin\\java.exe\" goto init\n\necho.\necho Error: JAVA_HOME is set to an invalid directory. >&2\necho JAVA_HOME = \"%JAVA_HOME%\" >&2\necho Please set the JAVA_HOME variable in your environment to match the >&2\necho location of your Java installation. >&2\necho.\ngoto error\n\n@REM ==== END VALIDATION ====\n\n:init\n\n@REM Find the project base dir, i.e. the directory that contains the folder \".mvn\".\n@REM Fallback to current working directory if not found.\n\nset MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%\nIF NOT \"%MAVEN_PROJECTBASEDIR%\"==\"\" goto endDetectBaseDir\n\nset EXEC_DIR=%CD%\nset WDIR=%EXEC_DIR%\n:findBaseDir\nIF EXIST \"%WDIR%\"\\.mvn goto baseDirFound\ncd ..\nIF \"%WDIR%\"==\"%CD%\" goto baseDirNotFound\nset WDIR=%CD%\ngoto findBaseDir\n\n:baseDirFound\nset MAVEN_PROJECTBASEDIR=%WDIR%\ncd \"%EXEC_DIR%\"\ngoto endDetectBaseDir\n\n:baseDirNotFound\nset MAVEN_PROJECTBASEDIR=%EXEC_DIR%\ncd \"%EXEC_DIR%\"\n\n:endDetectBaseDir\n\nIF NOT EXIST \"%MAVEN_PROJECTBASEDIR%\\.mvn\\jvm.config\" goto endReadAdditionalConfig\n\n@setlocal EnableExtensions EnableDelayedExpansion\nfor /F \"usebackq delims=\" %%a in (\"%MAVEN_PROJECTBASEDIR%\\.mvn\\jvm.config\") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a\n@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%\n\n:endReadAdditionalConfig\n\nSET MAVEN_JAVA_EXE=\"%JAVA_HOME%\\bin\\java.exe\"\n\nset WRAPPER_JAR=\"%MAVEN_PROJECTBASEDIR%\\.mvn\\wrapper\\maven-wrapper.jar\"\nset WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain\n\n%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% \"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%\" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*\nif ERRORLEVEL 1 goto error\ngoto end\n\n:error\nset ERROR_CODE=1\n\n:end\n@endlocal & set ERROR_CODE=%ERROR_CODE%\n\nif not \"%MAVEN_SKIP_RC%\" == \"\" goto skipRcPost\n@REM check for post script, once with legacy .bat ending and once with .cmd ending\nif exist \"%HOME%\\mavenrc_post.bat\" call \"%HOME%\\mavenrc_post.bat\"\nif exist \"%HOME%\\mavenrc_post.cmd\" call \"%HOME%\\mavenrc_post.cmd\"\n:skipRcPost\n\n@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'\nif \"%MAVEN_BATCH_PAUSE%\" == \"on\" pause\n\nif \"%MAVEN_TERMINATE_CMD%\" == \"on\" exit %ERROR_CODE%\n\nexit /B %ERROR_CODE%\n"
  },
  {
    "path": "ti-continuation-token/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>de.philipphauer</groupId>\n    <artifactId>ti-continuation-token</artifactId>\n    <version>1.0-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <kotlin.version>1.2.21</kotlin.version>\n        <junit5.version>5.0.2</junit5.version>\n        <http4k.version>3.0.1</http4k.version>\n        <kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.jetbrains.kotlin</groupId>\n            <artifactId>kotlin-stdlib-jdk8</artifactId>\n            <version>${kotlin.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.http4k</groupId>\n            <artifactId>http4k-core</artifactId>\n            <version>${http4k.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.http4k</groupId>\n            <artifactId>http4k-server-jetty</artifactId>\n            <version>${http4k.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.http4k</groupId>\n            <artifactId>http4k-format-jackson</artifactId>\n            <version>${http4k.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-jdbc</artifactId>\n            <version>5.0.1.RELEASE</version>\n        </dependency>\n        <dependency>\n            <groupId>com.h2database</groupId>\n            <artifactId>h2</artifactId>\n            <version>1.4.196</version>\n        </dependency>\n        <dependency>\n            <groupId>org.jetbrains.kotlin</groupId>\n            <artifactId>kotlin-reflect</artifactId>\n            <version>${kotlin.version}</version>\n        </dependency>\n\n\n        <!-- test -->\n        <dependency>\n            <groupId>org.junit.jupiter</groupId>\n            <artifactId>junit-jupiter-api</artifactId>\n            <version>${junit5.version}</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.assertj</groupId>\n            <artifactId>assertj-core</artifactId>\n            <version>3.8.0</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.junit.jupiter</groupId>\n            <artifactId>junit-jupiter-params</artifactId>\n            <version>5.0.2</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.nhaarman</groupId>\n            <artifactId>mockito-kotlin</artifactId>\n            <version>1.5.0</version>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <sourceDirectory>src/main/kotlin</sourceDirectory>\n        <testSourceDirectory>src/test/kotlin</testSourceDirectory>\n\n        <plugins>\n            <plugin>\n                <groupId>org.jetbrains.kotlin</groupId>\n                <artifactId>kotlin-maven-plugin</artifactId>\n                <version>${kotlin.version}</version>\n                <executions>\n                    <execution>\n                        <id>compile</id>\n                        <phase>compile</phase>\n                        <goals>\n                            <goal>compile</goal>\n                        </goals>\n                    </execution>\n                    <execution>\n                        <id>test-compile</id>\n                        <phase>test-compile</phase>\n                        <goals>\n                            <goal>test-compile</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <artifactId>maven-surefire-plugin</artifactId>\n                <version>2.19</version>\n                <dependencies>\n                    <dependency>\n                        <groupId>org.junit.platform</groupId>\n                        <artifactId>junit-platform-surefire-provider</artifactId>\n                        <version>1.0.2</version>\n                    </dependency>\n                    <dependency>\n                        <groupId>org.junit.jupiter</groupId>\n                        <artifactId>junit-jupiter-engine</artifactId>\n                        <version>${junit5.version}</version>\n                    </dependency>\n                </dependencies>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-shade-plugin</artifactId>\n                <version>3.1.0</version>\n                <configuration>\n                    <transformers>\n                        <transformer\n                                implementation=\"org.apache.maven.plugins.shade.resource.ManifestResourceTransformer\">\n                            <mainClass>de.philipphauer.blog.pagination.MainKt</mainClass>\n                        </transformer>\n                    </transformers>\n                </configuration>\n                <executions>\n                    <execution>\n                        <phase>package</phase>\n                        <goals>\n                            <goal>shade</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "ti-continuation-token/src/main/kotlin/de/philipphauer/blog/pagination/DesignDAO.kt",
    "content": "package de.philipphauer.blog.pagination\n\nimport de.philipphauer.blog.pagination.token.ContinuationToken\nimport de.philipphauer.blog.pagination.token.Page\nimport de.philipphauer.blog.pagination.token.createPage\nimport org.springframework.jdbc.core.JdbcTemplate\nimport java.sql.ResultSet\nimport java.time.Clock\nimport java.time.Instant\nimport javax.sql.DataSource\n\n\nclass DesignDAO(\n    dataSource: DataSource,\n    /** using the timestamp of the application (instead of the database) is only appropriate\n     *  if the application also exclusively sets and updates the timestamp */\n    private val clock: Clock\n) {\n\n    private val template = JdbcTemplate(dataSource)\n\n    fun getDesignsSince(modifiedSince: Instant, pageSize: Int): Page<DesignEntity> {\n        val sql = \"\"\"SELECT * FROM designs\n            WHERE dateModified >= FROM_UNIXTIME(${modifiedSince.epochSecond})\n            AND dateModified < FROM_UNIXTIME(${clock.instant().epochSecond})\n            ORDER BY dateModified asc, id asc\n            LIMIT $pageSize;\"\"\"\n        val designs = template.query(sql, this::mapToDesign)\n        return createPage(entities = designs, previousToken = null, pageSize = pageSize)\n    }\n\n    fun getNextDesignPage(token: ContinuationToken, pageSize: Int): Page<DesignEntity> {\n        val sql = \"\"\"SELECT * FROM designs\n            WHERE (\n              dateModified > FROM_UNIXTIME(${token.timestamp})\n              OR (dateModified = FROM_UNIXTIME(${token.timestamp}) AND id > ${token.id})\n            )\n            AND dateModified < FROM_UNIXTIME(${clock.instant().epochSecond})\n            ORDER BY dateModified asc, id asc\n            LIMIT $pageSize;\"\"\"\n        val designs = template.query(sql, this::mapToDesign)\n        return createPage(entities = designs, previousToken = token, pageSize = pageSize)\n    }\n\n    private fun mapToDesign(rs: ResultSet, rowNum: Int) = DesignEntity(\n        id = rs.getString(\"id\"),\n        title = rs.getString(\"title\"),\n        imageUrl = rs.getString(\"imageUrl\"),\n        dateModified = rs.getTimestamp(\"dateModified\").toInstant()\n    )\n}\n"
  },
  {
    "path": "ti-continuation-token/src/main/kotlin/de/philipphauer/blog/pagination/DesignEntity.kt",
    "content": "package de.philipphauer.blog.pagination\n\nimport de.philipphauer.blog.pagination.token.Pageable\nimport java.time.Instant\n\ndata class DesignEntity(\n    override val id: String,\n    val title: String,\n    val imageUrl: String,\n    private val dateModified: Instant\n) : Pageable {\n    override val timestamp = dateModified.epochSecond\n}"
  },
  {
    "path": "ti-continuation-token/src/main/kotlin/de/philipphauer/blog/pagination/DesignResource.kt",
    "content": "package de.philipphauer.blog.pagination\n\nimport com.fasterxml.jackson.module.kotlin.jacksonObjectMapper\nimport de.philipphauer.blog.pagination.token.toContinuationToken\nimport org.http4k.core.Request\nimport org.http4k.core.Response\nimport org.http4k.core.Status\nimport java.time.Instant\n\nclass DesignResource(private val dao: DesignDAO) {\n\n    fun getDesigns(request: Request): Response {\n        val modifiedSince = request.query(\"modifiedSince\").toInstantOrNull()\n        val token = request.query(\"continuationToken\")?.toContinuationToken()\n        val pageSize = request.query(\"pageSize\")?.toInt() ?: 3\n        val page = when {\n            modifiedSince == null && token == null -> dao.getDesignsSince(Instant.ofEpochSecond(0), pageSize)\n            modifiedSince != null && token == null -> dao.getDesignsSince(modifiedSince, pageSize)\n            modifiedSince == null && token != null -> dao.getNextDesignPage(token, pageSize)\n            else -> return Response(Status.BAD_REQUEST)\n        }\n        val dto = PageDTO(\n            designs = page.entities.map(::mapToDTO),\n            continuationToken = page.token?.toString(),\n            hasNext = page.hasNext,\n            nextPage = page.token?.let { \"http://localhost:8000/designs?pageSize=$pageSize&continuationToken=${page.token}\" }\n        )\n        return Response(Status.OK)\n            .header(\"Content-Type\", \"application/json;charset=UTF-8\")\n            .body(dto.toJson())\n    }\n}\n\nprivate fun String?.toInstantOrNull() = when (this) {\n    null -> null\n    else -> Instant.ofEpochSecond(this.toLong())\n}\n\nprivate fun mapToDTO(entity: DesignEntity) = DesignDTO(\n    id = entity.id,\n    title = entity.title,\n    imageUrl = entity.imageUrl,\n    dateModified = entity.timestamp\n)\n\ndata class DesignDTO(\n    val id: String,\n    val title: String,\n    val imageUrl: String,\n    val dateModified: Long\n)\n\ndata class PageDTO(\n    val designs: List<DesignDTO>,\n    val continuationToken: String?,\n    val nextPage: String?,\n    val hasNext: Boolean\n)\n\nprivate val mapper = jacksonObjectMapper()\nprivate fun PageDTO.toJson() = mapper.writeValueAsString(this)\n\n"
  },
  {
    "path": "ti-continuation-token/src/main/kotlin/de/philipphauer/blog/pagination/Main.kt",
    "content": "package de.philipphauer.blog.pagination\n\nimport de.philipphauer.blog.pagination.util.DesignDatabaseUtil\nimport de.philipphauer.blog.pagination.util.FunctionsMySQL\nimport org.eclipse.jetty.server.NCSARequestLog\nimport org.eclipse.jetty.server.Server\nimport org.h2.jdbcx.JdbcDataSource\nimport org.http4k.core.Method\nimport org.http4k.routing.bind\nimport org.http4k.routing.routes\nimport org.http4k.server.Jetty\nimport org.http4k.server.asServer\nimport org.springframework.core.io.ClassPathResource\nimport org.springframework.jdbc.datasource.init.ScriptUtils\nimport java.time.Clock\nimport java.time.Instant\n\nfun main(args: Array<String>) {\n    val resource = bootstrapDesignResource()\n\n    val routingHandler = routes(\n        \"/designs\" bind Method.GET to resource::getDesigns\n    )\n    val jetty = Server(8000).apply {\n        requestLog = NCSARequestLog()\n    }\n    val server = routingHandler.asServer(Jetty(jetty)).start()\n\n    println(\"Try http://localhost:8000/designs?pageSize=3 and click on the nextPage URL\")\n    println(\"or http://localhost:8000/designs?pageSize=3&modifiedSince=1512757072\")\n    server.block()\n}\n\nprivate fun bootstrapDesignResource(): DesignResource {\n    val dataSource = JdbcDataSource().apply {\n        user = \"sa\"\n        password = \"\"\n        setURL(\"jdbc:h2:mem:access;MODE=MySQL;DB_CLOSE_DELAY=-1\")\n    }\n    FunctionsMySQL.register(dataSource.connection)\n    ScriptUtils.executeSqlScript(dataSource.connection, ClassPathResource(\"create-designs-table.sql\"))\n\n    DesignDatabaseUtil(dataSource).createDesigns(amount = 7, startDate = Instant.ofEpochSecond(1512757070))\n\n    val dao = DesignDAO(dataSource, Clock.systemUTC())\n    return DesignResource(dao)\n}\n\n"
  },
  {
    "path": "ti-continuation-token/src/main/kotlin/de/philipphauer/blog/pagination/token/Model.kt",
    "content": "package de.philipphauer.blog.pagination.token\n\ninterface Pageable {\n    val id: String\n    val timestamp: Long\n}\n\ndata class ContinuationToken(\n    val timestamp: Long,\n    val id: String\n) {\n    override fun toString() = \"${timestamp}_$id\"\n}\n\ndata class Page<out T : Pageable>(\n    val entities: List<T>,\n    val token: ContinuationToken?,\n    val hasNext: Boolean\n)"
  },
  {
    "path": "ti-continuation-token/src/main/kotlin/de/philipphauer/blog/pagination/token/Pagination.kt",
    "content": "package de.philipphauer.blog.pagination.token\n\n@Throws(InvalidContinuationTokenException::class)\nfun String?.toContinuationToken(): ContinuationToken? {\n    this ?: return null\n    val parts = this.split(\"_\")\n    if (parts.size != 2) {\n        throw createException(this, null)\n    }\n    try {\n        val timestamp = java.lang.Long.parseUnsignedLong(parts[0])\n        val id = parts[1]\n        return ContinuationToken(timestamp, id)\n    } catch (ex: Exception) {\n        throw createException(this, ex)\n    }\n}\n\nfun <T : Pageable> createPage(\n    entities: List<T>,\n    previousToken: ContinuationToken?,\n    pageSize: Int\n): Page<T> = Page(\n    entities = entities,\n    token = if (entities.isEmpty()) previousToken else createToken(entities),\n    hasNext = entities.size >= pageSize\n)\n\nprivate fun <T : Pageable> createToken(entities: List<T>): ContinuationToken {\n    val lastEntity = entities.last()\n    return ContinuationToken(lastEntity.timestamp, lastEntity.id)\n}\n\nprivate fun createException(token: String, ex: Exception?): InvalidContinuationTokenException {\n    return InvalidContinuationTokenException(\"Invalid token '$token'\", ex)\n}\n\nclass InvalidContinuationTokenException(msg: String, cause: Exception?) : RuntimeException(msg, cause)"
  },
  {
    "path": "ti-continuation-token/src/main/kotlin/de/philipphauer/blog/pagination/util/DesignDatabaseUtil.kt",
    "content": "package de.philipphauer.blog.pagination.util\n\nimport org.springframework.jdbc.core.JdbcTemplate\nimport java.time.Instant\nimport javax.sql.DataSource\n\nclass DesignDatabaseUtil(dataSource: DataSource) {\n\n    private val utilTemplate = JdbcTemplate(dataSource)\n\n    fun createDesigns(amount: Int, startDate: Instant = Instant.now()) {\n        val values = (1..amount).mapIndexed { i, _ ->\n            arrayOf(i, \"Cat $i\", \"http://domain.de/cat$i.jpg\", startDate.plusSeconds(i.toLong()).epochSecond)\n        }\n        utilTemplate.batchUpdate(\n            \"INSERT INTO designs (id, title, imageUrl, dateModified) VALUES (?, ?, ?, FROM_UNIXTIME(?))\",\n            values\n        )\n    }\n\n    fun insertDesigns(designData: List<Pair<String, Long>>) {\n        val values = designData.map { (id, timestamp) ->\n            arrayOf(id, \"Cat $id\", \"http://domain.de/cat$id.jpg\", timestamp)\n        }\n        utilTemplate.batchUpdate(\n            \"INSERT INTO designs (id, title, imageUrl, dateModified) VALUES (?, ?, ?, FROM_UNIXTIME(?))\",\n            values\n        )\n    }\n\n    fun removeAllDesigns() {\n        utilTemplate.execute(\"TRUNCATE TABLE designs;\")\n    }\n\n    fun update(id: String, now: Instant) {\n        val newTitle = \"Cat $id (UPDATED)\"\n        utilTemplate.update(\"\"\"UPDATE designs\n            SET dateModified = FROM_UNIXTIME(?),\n            title = ?\n            WHERE id = ?;\"\"\", now.epochSecond, newTitle, id)\n    }\n}"
  },
  {
    "path": "ti-continuation-token/src/main/kotlin/de/philipphauer/blog/pagination/util/FunctionsMySQL.kt",
    "content": "package de.philipphauer.blog.pagination.util\n\nimport org.h2.util.StringUtils\nimport java.sql.Connection\nimport java.sql.SQLException\nimport java.text.SimpleDateFormat\nimport java.util.Date\nimport java.util.Locale\n\n/**\n * https://github.com/h2database/h2database/blob/master/h2/src/main/org/h2/mode/FunctionsMySQL.java\n * This class implements some MySQL-specific functions.\n *\n * @author Jason Brittain\n * @author Thomas Mueller\n */\nobject FunctionsMySQL {\n\n    /**\n     * The date format of a MySQL formatted date/time.\n     * Example: 2008-09-25 08:40:59\n     */\n    private val DATE_TIME_FORMAT = \"yyyy-MM-dd HH:mm:ss\"\n\n    /**\n     * Format replacements for MySQL date formats.\n     * See\n     * http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_date-format\n     */\n    private val FORMAT_REPLACE = arrayOf(\n        \"%a\",\n        \"EEE\",\n        \"%b\",\n        \"MMM\",\n        \"%c\",\n        \"MM\",\n        \"%d\",\n        \"dd\",\n        \"%e\",\n        \"d\",\n        \"%H\",\n        \"HH\",\n        \"%h\",\n        \"hh\",\n        \"%I\",\n        \"hh\",\n        \"%i\",\n        \"mm\",\n        \"%j\",\n        \"DDD\",\n        \"%k\",\n        \"H\",\n        \"%l\",\n        \"h\",\n        \"%M\",\n        \"MMMM\",\n        \"%m\",\n        \"MM\",\n        \"%p\",\n        \"a\",\n        \"%r\",\n        \"hh:mm:ss a\",\n        \"%S\",\n        \"ss\",\n        \"%s\",\n        \"ss\",\n        \"%T\",\n        \"HH:mm:ss\",\n        \"%W\",\n        \"EEEE\",\n        \"%w\",\n        \"F\",\n        \"%Y\",\n        \"yyyy\",\n        \"%y\",\n        \"yy\",\n        \"%%\",\n        \"%\"\n    )\n\n    /**\n     * Register the functionality in the database.\n     * Nothing happens if the functions are already registered.\n     *\n     * @param conn the connection\n     */\n    @Throws(SQLException::class)\n    fun register(conn: Connection) {\n        val init = arrayOf(\"UNIX_TIMESTAMP\", \"unixTimestamp\", \"FROM_UNIXTIME\", \"fromUnixTime\", \"DATE\", \"date\")\n        val stat = conn.createStatement()\n        var i = 0\n        while (i < init.size) {\n            val alias = init[i]\n            val method = init[i + 1]\n            stat.execute(\n                \"CREATE ALIAS IF NOT EXISTS \" + alias +\n                        \" FOR \\\"\" + FunctionsMySQL::class.java!!.name + \".\" + method + \"\\\"\"\n            )\n            i += 2\n        }\n    }\n\n    /**\n     * Get the seconds since 1970-01-01 00:00:00 UTC.\n     * See\n     * http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_unix-timestamp\n     *\n     * @return the current timestamp in seconds (not milliseconds).\n     */\n    @JvmStatic\n    fun unixTimestamp(): Int {\n        return (System.currentTimeMillis() / 1000L).toInt()\n    }\n\n    /**\n     * Get the seconds since 1970-01-01 00:00:00 UTC of the given timestamp.\n     * See\n     * http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_unix-timestamp\n     *\n     * @param timestamp the timestamp\n     * @return the current timestamp in seconds (not milliseconds).\n     */\n    @JvmStatic\n    fun unixTimestamp(timestamp: java.sql.Timestamp): Int {\n        return (timestamp.time / 1000L).toInt()\n    }\n\n    /**\n     * See\n     * http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_from-unixtime\n     *\n     * @param seconds The current timestamp in seconds.\n     * @return a formatted date/time String in the format \"yyyy-MM-dd HH:mm:ss\".\n     */\n    @JvmStatic\n    fun fromUnixTime(seconds: Int): String {\n        val formatter = SimpleDateFormat(\n            DATE_TIME_FORMAT,\n            Locale.ENGLISH\n        )\n        return formatter.format(Date(seconds * 1000L))\n    }\n\n    /**\n     * See\n     * http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_from-unixtime\n     *\n     * @param seconds The current timestamp in seconds.\n     * @param format The format of the date/time String to return.\n     * @return a formatted date/time String in the given format.\n     */\n    @JvmStatic\n    fun fromUnixTime(seconds: Int, format: String): String {\n        var format = format\n        format = convertToSimpleDateFormat(format)\n        val formatter = SimpleDateFormat(format, Locale.ENGLISH)\n        return formatter.format(Date(seconds * 1000L))\n    }\n\n    private fun convertToSimpleDateFormat(format: String): String {\n        var format = format\n        val replace = FORMAT_REPLACE\n        var i = 0\n        while (i < replace.size) {\n            format = StringUtils.replaceAll(format, replace[i], replace[i + 1])\n            i += 2\n        }\n        return format\n    }\n\n    /**\n     * See\n     * http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_date\n     * This function is dependent on the exact formatting of the MySQL date/time\n     * string.\n     *\n     * @param dateTime The date/time String from which to extract just the date\n     * part.\n     * @return the date part of the given date/time String argument.\n     */\n    @JvmStatic\n    fun date(dateTime: String?): String? {\n        if (dateTime == null) {\n            return null\n        }\n        val index = dateTime!!.indexOf(' ')\n        return if (index != -1) {\n            dateTime!!.substring(0, index)\n        } else dateTime\n    }\n\n}"
  },
  {
    "path": "ti-continuation-token/src/main/resources/create-designs-table.sql",
    "content": "CREATE TABLE designs (\n  id           INT AUTO_INCREMENT PRIMARY KEY,\n  title        VARCHAR(100) NOT NULL,\n  imageUrl     VARCHAR(100) NOT NULL,\n  dateModified TIMESTAMP(0) NOT NULL\n);"
  },
  {
    "path": "ti-continuation-token/src/test/kotlin/de/philipphauer/blog/pagination/Common.kt",
    "content": "package de.philipphauer.blog.pagination\n\nimport com.fasterxml.jackson.databind.DeserializationFeature\nimport com.fasterxml.jackson.module.kotlin.jacksonObjectMapper\nimport org.http4k.core.Response\n\nval mapper = jacksonObjectMapper().apply {\n    configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)\n}\n\nfun Response.toPageDTO(): PageDTO = mapper.readValue(bodyString(), PageDTO::class.java)"
  },
  {
    "path": "ti-continuation-token/src/test/kotlin/de/philipphauer/blog/pagination/DesignResourceTest.kt",
    "content": "package de.philipphauer.blog.pagination\n\nimport com.nhaarman.mockito_kotlin.doReturn\nimport com.nhaarman.mockito_kotlin.mock\nimport com.nhaarman.mockito_kotlin.whenever\nimport de.philipphauer.blog.pagination.util.DesignDatabaseUtil\nimport de.philipphauer.blog.pagination.util.FunctionsMySQL\nimport org.assertj.core.api.Assertions.assertThat\nimport org.h2.jdbcx.JdbcDataSource\nimport org.http4k.core.Method\nimport org.http4k.core.Request\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.TestInstance\nimport org.springframework.core.io.ClassPathResource\nimport org.springframework.jdbc.datasource.init.ScriptUtils\nimport java.time.Clock\nimport java.time.Instant\n\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\ninternal class DesignResourceTest {\n\n    private val util = DesignDatabaseUtil(dataSource)\n\n    init {\n        FunctionsMySQL.register(dataSource.connection)\n        ScriptUtils.executeSqlScript(dataSource.connection, ClassPathResource(\"create-designs-table.sql\"))\n    }\n\n    @Test\n    fun `page through two pages`() {\n        val resource = createDesignResource()\n        val startDate = Instant.ofEpochSecond(1512757070)\n        util.createDesigns(amount = 5, startDate = startDate)\n\n        val firstPageResponse = resource.getDesigns(Request(Method.GET, \"/designs?pageSize=3\"))\n\n        val firstPage = firstPageResponse.toPageDTO()\n        assertThat(firstPage.continuationToken).isNotNull()\n        assertThat(firstPage.hasNext).isTrue()\n        assertThat(firstPage.nextPage).isNotNull()\n        assertThat(firstPage.designs).containsExactly(\n            DesignDTO(id = \"0\", title = \"Cat 0\", imageUrl = \"http://domain.de/cat0.jpg\", dateModified = 1512757070),\n            DesignDTO(id = \"1\", title = \"Cat 1\", imageUrl = \"http://domain.de/cat1.jpg\", dateModified = 1512757071),\n            DesignDTO(id = \"2\", title = \"Cat 2\", imageUrl = \"http://domain.de/cat2.jpg\", dateModified = 1512757072)\n        )\n\n        val secondPageResponse = resource.getDesigns(Request(Method.GET, firstPage.nextPage!!))\n        val secondPage = secondPageResponse.toPageDTO()\n        assertThat(secondPage.continuationToken).isNotNull()\n        assertThat(secondPage.hasNext).isFalse()\n        assertThat(secondPage.nextPage).isNotNull()\n        assertThat(secondPage.designs).containsExactly(\n            DesignDTO(id = \"3\", title = \"Cat 3\", imageUrl = \"http://domain.de/cat3.jpg\", dateModified = 1512757073),\n            DesignDTO(id = \"4\", title = \"Cat 4\", imageUrl = \"http://domain.de/cat4.jpg\", dateModified = 1512757074)\n        )\n    }\n\n    @Test\n    fun `start with a certain modifiedSince query parameter and page through two pages`() {\n        val resource = createDesignResource()\n        val modifiedSince: Long = 1512757072\n        val designData = listOf<Pair<String, Long>>(\n            \"0\" to 1512757070\n            , \"1\" to 1512757071\n            , \"2\" to modifiedSince\n            , \"3\" to 1512757073\n            , \"4\" to 1512757074\n            , \"5\" to 1512757075\n        )\n        util.insertDesigns(designData)\n\n        val firstPageResponse =\n            resource.getDesigns(Request(Method.GET, \"/designs?modifiedSince=$modifiedSince&pageSize=3\"))\n        val firstPage = firstPageResponse.toPageDTO()\n        assertThat(firstPage.continuationToken).isNotNull()\n        assertThat(firstPage.hasNext).isTrue()\n        assertThat(firstPage.nextPage).isNotNull()\n        assertThat(firstPage.designs).containsExactly(\n            DesignDTO(id = \"2\", title = \"Cat 2\", imageUrl = \"http://domain.de/cat2.jpg\", dateModified = 1512757072),\n            DesignDTO(id = \"3\", title = \"Cat 3\", imageUrl = \"http://domain.de/cat3.jpg\", dateModified = 1512757073),\n            DesignDTO(id = \"4\", title = \"Cat 4\", imageUrl = \"http://domain.de/cat4.jpg\", dateModified = 1512757074)\n        )\n\n        val secondPageResponse = resource.getDesigns(Request(Method.GET, firstPage.nextPage!!))\n        val secondPage = secondPageResponse.toPageDTO()\n        assertThat(secondPage.continuationToken).isNotNull()\n        assertThat(secondPage.hasNext).isFalse()\n        assertThat(secondPage.nextPage).isNotNull()\n        assertThat(secondPage.designs).containsExactly(\n            DesignDTO(id = \"5\", title = \"Cat 5\", imageUrl = \"http://domain.de/cat5.jpg\", dateModified = 1512757075)\n        )\n    }\n\n    /**\n     * we miss an element when the following three things are happening within a single second (and given that this is the current second):\n    a) element 3's timestamp is set to now (99),\n    b) the last page with element 3 is returned (token `3_99`) and\n    c) element 2's timestamp is also set to now (99).\n    (given a timestamp column with second precision. with ms precision, this three things have to happen within one ms)\n    solution: add condition `AND timestamp > now()` to the where clause.\n     */\n    @Test\n    fun `dont miss elements when updates are happening before the last page`() {\n        val serverClock = mock<Clock>()\n        val resource = createDesignResource(serverClock)\n        val designData = listOf<Pair<String, Long>>(\n            \"1\" to 10\n            , \"2\" to 20\n            , \"3\" to 30\n        )\n        util.insertDesigns(designData)\n        val client = PaginationClient(resource = resource, pageSize = 3)\n\n        val sameSecond = Instant.ofEpochSecond(99)\n        util.update(id = \"3\", now = sameSecond)\n        whenever(serverClock.instant()).doReturn(sameSecond) //to override the server's \"now()\".\n        client.retrieveNextPageAndRememberResult()\n        util.update(id = \"2\", now = sameSecond) //this must not be missed in the next request\n        //simulate a passed second (no Thread.sleep() in test required). in reality, this would be a new pagination run\n        whenever(serverClock.instant()).doReturn(sameSecond.plusSeconds(1))\n        client.retrieveNextPageAndRememberResult()\n\n        val allDesigns = client.getAllRetrievedDesigns()\n        assertThat(allDesigns).containsOnly(\n            DesignDTO(id = \"1\", title = \"Cat 1\", imageUrl = \"http://domain.de/cat1.jpg\", dateModified = 10)\n            , DesignDTO(id = \"2\", title = \"Cat 2\", imageUrl = \"http://domain.de/cat2.jpg\", dateModified = 20)\n            , DesignDTO(id = \"3\", title = \"Cat 3 (UPDATED)\", imageUrl = \"http://domain.de/cat3.jpg\", dateModified = sameSecond.epochSecond)\n            //this must not be missed:\n            , DesignDTO(id = \"2\", title = \"Cat 2 (UPDATED)\", imageUrl = \"http://domain.de/cat2.jpg\", dateModified = sameSecond.epochSecond)\n        )\n    }\n\n    @Test\n    fun `dont return elements with current timestamp - request with touched since`() {\n        val serverClock = mock<Clock>()\n        val resource = createDesignResource(serverClock)\n        val sameSecond = Instant.ofEpochSecond(99)\n        val designData = listOf<Pair<String, Long>>(\n            \"1\" to 10\n            , \"2\" to 20\n            , \"3\" to sameSecond.epochSecond\n        )\n        util.insertDesigns(designData)\n\n        whenever(serverClock.instant()).doReturn(sameSecond)\n        val response = resource.getDesigns(Request(Method.GET, \"/designs?pageSize=3&touchedSince=0\")).toPageDTO()\n\n        assertThat(response.continuationToken).isEqualTo(\"20_2\")\n        assertThat(response.designs).containsOnly(\n            DesignDTO(id = \"1\", title = \"Cat 1\", imageUrl = \"http://domain.de/cat1.jpg\", dateModified = 10)\n            , DesignDTO(id = \"2\", title = \"Cat 2\", imageUrl = \"http://domain.de/cat2.jpg\", dateModified = 20)\n        )\n    }\n\n    @Test\n    fun `dont return elements with current timestamp - request with token`() {\n        val serverClock = mock<Clock>()\n        val resource = createDesignResource(serverClock)\n        val sameSecond = Instant.ofEpochSecond(99)\n        val designData = listOf<Pair<String, Long>>(\n            \"1\" to 10\n            , \"2\" to 20\n            , \"3\" to sameSecond.epochSecond\n        )\n        util.insertDesigns(designData)\n\n        whenever(serverClock.instant()).doReturn(sameSecond)\n        val response = resource.getDesigns(Request(Method.GET, \"/designs?pageSize=3&continuationToken=1_10\")).toPageDTO()\n\n        assertThat(response.continuationToken).isEqualTo(\"20_2\")\n        assertThat(response.designs).containsOnly(\n            DesignDTO(id = \"1\", title = \"Cat 1\", imageUrl = \"http://domain.de/cat1.jpg\", dateModified = 10)\n            , DesignDTO(id = \"2\", title = \"Cat 2\", imageUrl = \"http://domain.de/cat2.jpg\", dateModified = 20)\n        )\n    }\n\n    @BeforeEach\n    fun cleanup() {\n        util.removeAllDesigns()\n    }\n\n    private fun createDesignResource(clock: Clock = Clock.systemUTC()): DesignResource {\n        val dao = DesignDAO(dataSource, clock)\n        return DesignResource(dao)\n    }\n}\n\nprivate val dataSource = JdbcDataSource().apply {\n    user = \"sa\"\n    password = \"\"\n    setURL(\"jdbc:h2:mem:access;MODE=MySQL;DB_CLOSE_DELAY=-1\")\n}\n\n"
  },
  {
    "path": "ti-continuation-token/src/test/kotlin/de/philipphauer/blog/pagination/PaginationClient.kt",
    "content": "package de.philipphauer.blog.pagination\n\nimport org.http4k.core.Method\nimport org.http4k.core.Request\n\nclass PaginationClient(\n    private val pageSize: Int,\n    private val resource: DesignResource\n) {\n    private val retrievedDesigns = mutableListOf<DesignDTO>()\n    private var nextContinuationToken: String? = null\n    private var requestCounter = 0\n\n    fun retrieveNextPageAndRememberResult() {\n        val page = if (nextContinuationToken == null) {\n            println(\"Request ${requestCounter++}: Without token\")\n            resource.getDesigns(Request(Method.GET, \"/designs?pageSize=$pageSize\")).toPageDTO()\n        } else {\n            println(\"Request ${requestCounter++}: With token $nextContinuationToken\")\n            resource.getDesigns(Request(Method.GET, \"/designs?pageSize=$pageSize&continuationToken=$nextContinuationToken\")).toPageDTO()\n        }\n        println(\"  Retrieved ${page.designs.size} designs: ${page.designs.map(DesignDTO::id)}. Next token will be ${page.continuationToken}\")\n        retrievedDesigns.addAll(page.designs)\n        nextContinuationToken = page.continuationToken\n    }\n\n    fun getAllRetrievedDesigns() = retrievedDesigns.toList()\n}"
  },
  {
    "path": "ti-continuation-token/src/test/kotlin/de/philipphauer/blog/pagination/token/PaginationTest.kt",
    "content": "package de.philipphauer.blog.pagination.token\n\nimport org.assertj.core.api.Assertions.assertThat\nimport org.assertj.core.api.Assertions.assertThatThrownBy\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.TestInstance\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.MethodSource\nimport org.junit.jupiter.params.provider.ValueSource\nimport java.util.stream.Stream\n\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\ninternal class PaginationTest {\n\n    @ParameterizedTest\n    @MethodSource(\"validTokenProvider\")\n    fun parse_valid(data: ValidTokenTestData) {\n        assertThat(data.token.toContinuationToken()).isEqualTo(data.continuationToken)\n    }\n\n    private fun validTokenProvider() = Stream.of(\n        ValidTokenTestData(\"1511443755_2\", ContinuationToken(1511443755, \"2\"))\n        , ValidTokenTestData(\"1463997600_10273521\", ContinuationToken(1463997600, \"10273521\"))\n        , ValidTokenTestData(\"151144375_1\", ContinuationToken(151144375, \"1\"))\n        // id can also be strings\n        , ValidTokenTestData(\"151144375_id\", ContinuationToken(151144375, \"id\"))\n        // timestamp can also have millisecond precision\n        , ValidTokenTestData(\"1511443755999_1\", ContinuationToken(1511443755999, \"1\"))\n        , ValidTokenTestData(null, null)\n    )\n\n    @ParameterizedTest\n    @ValueSource(\n        strings = [\n            \"asdf_1_1842521611\"\n            , \"asdf_1\"\n            , \"1511443755_sadfasd_1842521611\"\n            , \"1511443755_1_sadfasd\"\n            , \"\"\n            , \"__\"\n            , \"12__\"\n            , \"12__213\"\n            , \"_1231_213\"\n            , \"-1231_213\"\n            , \"-2_23\"\n        ]\n    )\n    fun parse_invalid(invalidToken: String) {\n        assertThatThrownBy { invalidToken.toContinuationToken() }\n            .isInstanceOf(InvalidContinuationTokenException::class.java)\n            .hasMessageStartingWith(\"Invalid token '$invalidToken'\")\n    }\n\n    @Test\n    fun onlyOnePage() {\n        val entities = listOf(\n            TestPageable(\"1\", 10),\n            TestPageable(\"2\", 20),\n            TestPageable(\"3\", 30)\n        )\n\n        val actualPage = createPage(entities, null, 10)\n\n        val expectedPage = Page(\n            entities = entities,\n            token = ContinuationToken(30, \"3\"),\n            hasNext = false\n        )\n        assertThat(actualPage).isEqualTo(expectedPage)\n    }\n\n    @Test\n    fun hasNextPage() {\n        val entities = listOf(\n            TestPageable(\"1\", 10),\n            TestPageable(\"2\", 20),\n            TestPageable(\"3\", 30)\n        )\n\n        val actualPage = createPage(entities, null, 3)\n\n        val expectedPage = Page(\n            entities = entities,\n            token = ContinuationToken(30, \"3\"),\n            hasNext = true\n        )\n        assertThat(actualPage).isEqualTo(expectedPage)\n    }\n\n    @Test\n    fun onlyOneElement() {\n        val entities = listOf(TestPageable(\"1\", 10))\n\n        val actualPage = createPage(entities, null, 3)\n\n        val expectedPage = Page(\n            entities = entities,\n            token = ContinuationToken(10, \"1\"),\n            hasNext = false\n        )\n        assertThat(actualPage).isEqualTo(expectedPage)\n    }\n\n    @Test\n    fun emptyPage() {\n        val entities = listOf<TestPageable>()\n\n        val actualPage = createPage(entities, null, 3)\n\n        val expectedPage = Page(entities, null, false)\n        assertThat(actualPage).isEqualTo(expectedPage)\n    }\n\n    @Test\n    fun emptyPage_returnReceivedTokenInCaseOfEmptyPage() {\n        val entities = listOf<TestPageable>()\n\n        val token = ContinuationToken(30, \"3\")\n        val actualPage = createPage(entities, token, 3)\n\n        val expectedPage = Page(entities, token, false)\n        assertThat(actualPage).isEqualTo(expectedPage)\n    }\n\n    @Test\n    fun returnANewTokenEvenIfAnTokenIsPassed() {\n        val entities = listOf(\n            TestPageable(\"4\", 40),\n            TestPageable(\"5\", 50),\n            TestPageable(\"6\", 60)\n        )\n\n        val actualPage = createPage(\n            entities,\n            ContinuationToken(30, \"3\"),\n            10\n        )\n\n        val expectedPage = Page(\n            entities = entities,\n            token = ContinuationToken(60, \"6\"),\n            hasNext = false\n        )\n        assertThat(actualPage).isEqualTo(expectedPage)\n    }\n\n}\n\ndata class TestPageable(\n    override val id: String,\n    override val timestamp: Long\n) : Pageable\n\ndata class ValidTokenTestData(\n    val token: String?,\n    val continuationToken: ContinuationToken?\n)"
  },
  {
    "path": "unit-tests-kotlin/.gitignore",
    "content": "target\n.idea\n*.iml"
  },
  {
    "path": "unit-tests-kotlin/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>de.philipphauer.blog</groupId>\n    <artifactId>unit-tests-kotlin</artifactId>\n    <version>1.0-SNAPSHOT</version>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>\n        <java.version>11</java.version>\n        <kotlin.version>1.4.0</kotlin.version>\n        <junit5.version>5.0.3</junit5.version>\n        <vaadin.version>8.3.0</vaadin.version>\n        <kotest.version>4.2.5</kotest.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>com.vaadin</groupId>\n            <artifactId>vaadin-server</artifactId>\n            <version>${vaadin.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.jetbrains.kotlin</groupId>\n            <artifactId>kotlin-reflect</artifactId>\n            <version>${kotlin.version}</version>\n        </dependency>\n\n        <!--  test -->\n        <dependency>\n            <groupId>org.assertj</groupId>\n            <artifactId>assertj-core</artifactId>\n            <version>3.11.1</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.junit.jupiter</groupId>\n            <artifactId>junit-jupiter</artifactId>\n            <version>5.5.2</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <version>4.12</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.testcontainers</groupId>\n            <artifactId>testcontainers</artifactId>\n            <version>1.5.0</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>io.mockk</groupId>\n            <artifactId>mockk</artifactId>\n            <version>1.9.3</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>io.kotest</groupId>\n            <artifactId>kotest-runner-junit5-jvm</artifactId>\n            <version>${kotest.version}</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>io.kotest</groupId>\n            <artifactId>kotest-assertions-core-jvm</artifactId>\n            <version>${kotest.version}</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>io.kotest</groupId>\n            <artifactId>kotest-property-jvm</artifactId>\n            <version>${kotest.version}</version>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>com.vaadin</groupId>\n                <artifactId>vaadin-bom</artifactId>\n                <version>${vaadin.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <sourceDirectory>src/main/kotlin</sourceDirectory>\n        <testSourceDirectory>src/test/kotlin</testSourceDirectory>\n        <plugins>\n            <plugin>\n                <artifactId>kotlin-maven-plugin</artifactId>\n                <groupId>org.jetbrains.kotlin</groupId>\n                <configuration>\n                    <args>\n                        <arg>-Xjsr305=strict</arg>\n                    </args>\n                </configuration>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-surefire-plugin</artifactId>\n                <version>2.22.2</version>\n            </plugin>\n        </plugins>\n    </build>\n</project>"
  },
  {
    "path": "unit-tests-kotlin/src/main/kotlin/com/phauer/unittestkotlin/MongoDAO.kt",
    "content": "package com.phauer.unittestkotlin\n\n//class under test\nclass MongoDAO(host: String, port: Int) {\n\n}"
  },
  {
    "path": "unit-tests-kotlin/src/test/kotlin/com/phauer/unittestkotlin/BackticksAndNestedClasses.kt",
    "content": "package com.phauer.unittestkotlin\n\nimport org.junit.jupiter.api.Test\n\nclass DesignControllerTest {\n    @Test\n    fun `design is removed from db`() {\n    }\n    @Test\n    fun `return 404 on invalid id parameter`() {\n    }\n\n    @Test\n    fun `return 401 if not authorized`() {\n    }\n}\n"
  },
  {
    "path": "unit-tests-kotlin/src/test/kotlin/com/phauer/unittestkotlin/DataClassAssertions.kt",
    "content": "package com.phauer.unittestkotlin\n\nimport io.kotest.assertions.asClue\nimport io.kotest.matchers.collections.shouldContainExactly\nimport io.kotest.matchers.equality.shouldBeEqualToIgnoringFields\nimport io.kotest.matchers.equality.shouldBeEqualToUsingFields\nimport io.kotest.matchers.shouldBe\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Test\n\nclass DataClassAssertions {\n\n    //Don't\n    @Test\n    fun test() {\n        val client = DesignClient()\n\n        val actualDesign = client.requestDesign(id = 1)\n\n        assertThat(actualDesign.id).isEqualTo(2)\n        assertThat(actualDesign.userId).isEqualTo(9)\n        assertThat(actualDesign.name).isEqualTo(\"Cat\")\n\n        /*\n        org.junit.ComparisonFailure: expected:<[2]> but was:<[1]>\n        Expected :2\n        Actual   :1\n         */\n    }\n\n    @Test\n    fun test_kotest() {\n        val client = DesignClient()\n\n        val actualDesign = client.requestDesign(id = 1)\n\n        actualDesign.id shouldBe 2 // ComparisonFailure\n        actualDesign.userId shouldBe 9\n        actualDesign.name shouldBe \"Cat\"\n\n        /*\n        org.opentest4j.AssertionFailedError: expected:<2> but was:<1>\n        Expected :2\n        Actual   :1\n         */\n    }\n\n    //Do\n    @Test\n    fun test2() {\n        val client = DesignClient()\n\n        val actualDesign = client.requestDesign(id = 1)\n\n        val expectedDesign = Design(\n            id = 2,\n            userId = 9,\n            name = \"Cat\"\n        )\n        assertThat(actualDesign).isEqualTo(expectedDesign)\n\n        /*\n        org.junit.ComparisonFailure: expected:<Design(id=[2], userId=9, name=Cat...> but was:<Design(id=[1], userId=9, name=Cat...>\n        Expected :Design(id=2, userId=9, name=Cat)\n        Actual   :Design(id=1, userId=9, name=Cat)\n         */\n    }\n\n    @Test\n    fun test2_kotest() {\n        val client = DesignClient()\n\n        val actualDesign = client.requestDesign(id = 1)\n\n        val expectedDesign = Design(\n            id = 2,\n            userId = 9,\n            name = \"Cat\"\n        )\n        actualDesign shouldBe expectedDesign\n\n        /*\n        org.opentest4j.AssertionFailedError: data class diff for de.philipphauer.blog.unittestkotlin.Design\n        └ id: expected:<2> but was:<1>\n\n        expected:<Design(id=2, userId=9, name=Cat)> but was:<Design(id=1, userId=9, name=Cat)>\n        Expected :Design(id=2, userId=9, name=Cat)\n        Actual   :Design(id=1, userId=9, name=Cat)\n         */\n    }\n\n    //Do\n    @Test\n    fun lists() {\n        val client = DesignClient()\n\n        val actualDesigns = client.getAllDesigns()\n\n        assertThat(actualDesigns).containsExactly(\n            Design(\n                id = 1,\n                userId = 9,\n                name = \"Cat\"\n            ),\n            Design(\n                id = 2,\n                userId = 4,\n                name = \"Dog\"\n            )\n        )\n        /*\n        java.lang.AssertionError:\n        Expecting:\n          <[Design(id=1, userId=9, name=Cat),\n            Design(id=2, userId=4, name=Dogggg)]>\n        to contain exactly (and in same order):\n          <[Design(id=1, userId=9, name=Cat),\n            Design(id=2, userId=4, name=Dog)]>\n        but some elements were not found:\n          <[Design(id=2, userId=4, name=Dog)]>\n        and others were not expected:\n          <[Design(id=2, userId=4, name=Dogggg)]>\n         */\n    }\n\n    @Test\n    fun lists_kotest() {\n        val client = DesignClient()\n\n        val actualDesigns = client.getAllDesigns()\n\n        actualDesigns.shouldContainExactly(\n            Design(\n                id = 1,\n                userId = 9,\n                name = \"Cat\"\n            ),\n            Design(\n                id = 2,\n                userId = 4,\n                name = \"Dog\"\n            )\n        )\n        /*\n        java.lang.AssertionError: Expecting: [\n          Design(id=1, userId=9, name=Cat),\n          Design(id=2, userId=4, name=Dog)\n        ] but was: [\n          Design(id=1, userId=9, name=Cat),\n          Design(id=2, userId=4, name=Dogggg)\n        ]\n        Some elements were missing: [\n          Design(id=2, userId=4, name=Dog)\n        ] and some elements were unexpected: [\n          Design(id=2, userId=4, name=Dogggg)\n        ]\n         */\n    }\n\n    @Test\n    fun sophisticatedAssertions_single() {\n        val client = DesignClient()\n\n        val actualDesign = client.requestDesign(id = 1)\n\n        val expectedDesign = Design(\n            id = 2,\n            userId = 9,\n            name = \"Cat\"\n        )\n        assertThat(actualDesign).isEqualToIgnoringGivenFields(expectedDesign, \"id\")\n        assertThat(actualDesign).isEqualToComparingOnlyGivenFields(expectedDesign, \"userId\", \"name\")\n    }\n\n    @Test\n    fun sophisticatedAssertions_single_kotest() {\n        val client = DesignClient()\n\n        val actualDesign = client.requestDesign(id = 1)\n\n        val expectedDesign = Design(\n            id = 2,\n            userId = 9,\n            name = \"Cat\"\n        )\n        actualDesign.shouldBeEqualToIgnoringFields(expectedDesign, Design::id)\n        actualDesign.shouldBeEqualToUsingFields(expectedDesign, Design::userId, Design::name)\n    }\n\n    @Test\n    fun sophisticatedAssertions_lists() {\n        val client = DesignClient()\n\n        val actualDesigns = client.getAllDesigns()\n\n        assertThat(actualDesigns).usingElementComparatorIgnoringFields(\"dateCreated\").containsExactly(\n            Design(\n                id = 1,\n                userId = 9,\n                name = \"Cat\"\n            ),\n            Design(\n                id = 2,\n                userId = 4,\n                name = \"Dog\"\n            )\n        )\n        assertThat(actualDesigns).usingElementComparatorOnFields(\"userId\", \"name\").containsExactly(\n            Design(\n                id = 1,\n                userId = 9,\n                name = \"Cat\"\n            ),\n            Design(\n                id = 2,\n                userId = 4,\n                name = \"Dog\"\n            )\n        )\n    }\n\n    @Test\n    fun sophisticatedAssertions_lists_kotest() {\n        val client = DesignClient()\n\n        val actualDesigns = client.getAllDesigns()\n\n        // TODO doesn't seem to exist in kotest yet\n//        assertThat(actualDesigns).usingElementComparatorIgnoringFields(\"dateCreated\").containsExactly(\n//            Design(id = 1, userId = 9, name = \"Cat\", dateCreated = Instant.ofEpochSecond(1518278198)),\n//            Design(id = 2, userId = 4, name = \"Dogggg\", dateCreated = Instant.ofEpochSecond(1518279000))\n//        )\n//        assertThat(actualDesigns).usingElementComparatorOnFields(\"id\", \"name\").containsExactly(\n//            Design(id = 1, userId = 9, name = \"Cat\", dateCreated = Instant.ofEpochSecond(1518278198)),\n//            Design(id = 2, userId = 4, name = \"Dogggg\", dateCreated = Instant.ofEpochSecond(1518279000))\n//        )\n    }\n\n    @Test\n    fun grouping() {\n        val client = DesignClient()\n\n        val actualDesign = client.requestDesign(id = 1)\n\n        actualDesign.asClue {\n            it.id shouldBe 2\n            it.userId shouldBe 9\n            it.name shouldBe \"Cat\"\n        }\n        /**\n         * org.opentest4j.AssertionFailedError: Design(id=1, userId=9, name=Cat, dateCreated=2018-02-10T15:56:38Z)\n        expected:<2> but was:<1>\n        Expected :2\n        Actual   :1\n         */\n    }\n}\n\ndata class Design(\n    val id: Int,\n    val userId: Int,\n    val name: String\n)\n\nclass DesignClient {\n    fun requestDesign(id: Int) =\n        Design(\n            id = 1,\n            userId = 9,\n            name = \"Cat\"\n        )\n\n    fun getAllDesigns() = listOf(\n        Design(\n            id = 1,\n            userId = 9,\n            name = \"Cat\"\n        ),\n        Design(\n            id = 2,\n            userId = 4,\n            name = \"Dogggg\"\n        )\n    )\n}"
  },
  {
    "path": "unit-tests-kotlin/src/test/kotlin/com/phauer/unittestkotlin/HandlingState.kt",
    "content": "package com.phauer.unittestkotlin\n\nimport com.vaadin.navigator.View\nimport com.vaadin.ui.Button\nimport com.vaadin.ui.Panel\nimport io.kotest.matchers.shouldBe\nimport io.mockk.clearAllMocks\nimport io.mockk.mockk\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.TestInstance\n\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\nclass DesignViewTest {\n\n    private val dao: DesignDAO = mockk()\n\n    // the class under test has state\n    private lateinit var view: DesignView\n\n    @BeforeEach\n    fun init() {\n        clearAllMocks()\n        view = DesignView(dao)\n    }\n\n    @Test\n    fun changeButton() {\n        view.button.caption shouldBe \"Hi\"\n        view.changeButton()\n        view.button.caption shouldBe \"Hallo\"\n    }\n}\n\n// class with state\nclass DesignView(val dao: DesignDAO) : Panel(), View {\n\n    val button = Button(\"Hi\")\n\n    init {\n        content = button\n    }\n\n    fun changeButton() {\n        button.caption = \"Hallo\"\n    }\n}"
  },
  {
    "path": "unit-tests-kotlin/src/test/kotlin/com/phauer/unittestkotlin/IntroductionExample.kt",
    "content": "package com.phauer.unittestkotlin\n\n//import com.nhaarman.mockito_kotlin.mock\n//import com.nhaarman.mockito_kotlin.reset\n//import com.nhaarman.mockito_kotlin.whenever\n//import org.junit.Assert.assertEquals\n//import org.junit.Before\n//import org.junit.BeforeClass\n//import org.junit.Test\n//\n//class UserControllerTest {\n//    companion object {\n//        @JvmStatic private lateinit var controller: UserController\n//        @JvmStatic private lateinit var repo: UserRepository\n//        @BeforeClass @JvmStatic fun initialize() {\n//            repo = mock()\n//            controller = UserController(repo)\n//        }\n//    }\n//    @Test\n//    fun findUser_UserFoundAndHasCorrectValues() {\n//        `when`((repo.findUser(1))).thenReturn(User(1, \"Peter\"))\n//        val user = controller.getUser(1)\n//        assertEquals(user?.name, \"Peter\")\n//    }\n//    @Before\n//    fun clear(){\n//        reset(repo)\n//    }\n//}\n\nopen class UserRepository {\n    open fun findUser(id: Int): User? {\n        return User(id, \"Peter\")\n    }\n}\n\nclass UserController(\n    val repo: UserRepository\n) {\n    fun getUser(id: Int): User? {\n        return repo.findUser(id)\n    }\n}\n\ndata class User(\n    val id: Int,\n    val name: String\n)"
  },
  {
    "path": "unit-tests-kotlin/src/test/kotlin/com/phauer/unittestkotlin/KGenericContainer.kt",
    "content": "package com.phauer.unittestkotlin\n\nimport org.testcontainers.containers.GenericContainer\n\nclass KGenericContainer(imageName: String) : GenericContainer<KGenericContainer>(imageName)"
  },
  {
    "path": "unit-tests-kotlin/src/test/kotlin/com/phauer/unittestkotlin/MockHandling.kt",
    "content": "package com.phauer.unittestkotlin\n\nimport io.mockk.clearAllMocks\nimport io.mockk.mockk\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.RepeatedTest\nimport org.junit.jupiter.api.TestInstance\n\n// Do:\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\nclass DesignControllerTest_Mock {\n\n    private val dao: DesignDAO = mockk()\n    private val mapper: DesignMapper = mockk()\n    private val controller = DesignController(dao, mapper)\n\n    @BeforeEach\n    fun init() {\n        clearAllMocks()\n    }\n\n    // takes 210 ms\n    @RepeatedTest(300)\n    fun foo() {\n        controller.doSomething()\n    }\n}\n\n//Don't\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\nclass DesignControllerTest_RecreatingMocks {\n\n    private lateinit var dao: DesignDAO\n    private lateinit var mapper: DesignMapper\n    private lateinit var controller: DesignController\n\n    @BeforeEach\n    fun init() {\n        dao = mockk()\n        mapper = mockk()\n        controller = DesignController(dao, mapper)\n    }\n\n    // takes 1,5 s!\n    @RepeatedTest(300)\n    fun foo() {\n        controller.doSomething()\n    }\n}\n\n\nopen class DesignDAO\nopen class DesignMapper\nclass DesignController(val dao: DesignDAO, val mapper: DesignMapper) {\n    fun doSomething() {\n\n    }\n}\n"
  },
  {
    "path": "unit-tests-kotlin/src/test/kotlin/com/phauer/unittestkotlin/MongoDAOTestJUnit4.kt",
    "content": "package com.phauer.unittestkotlin\n\nimport org.junit.BeforeClass\nimport org.junit.Test\n\n//JUnit4. Don't:\nclass MongoDAOTestJUnit4 {\n\n    companion object {\n        @JvmStatic\n        private lateinit var mongo: KGenericContainer\n        @JvmStatic\n        private lateinit var mongoDAO: MongoDAO\n\n        @BeforeClass\n        @JvmStatic\n        fun initialize() {\n            mongo = KGenericContainer(\"mongo:3.4.3\").apply {\n                withExposedPorts(27017)\n                start()\n            }\n            mongoDAO = MongoDAO(host = mongo.containerIpAddress, port = mongo.getMappedPort(27017))\n        }\n    }\n\n    @Test\n    fun foo() {\n        // test mongoDAO\n    }\n}\n\n//JUnit4 -> java.lang.Exception: Method init() should be static\n//    @BeforeClass\n//    fun init(){\n//    }\n"
  },
  {
    "path": "unit-tests-kotlin/src/test/kotlin/com/phauer/unittestkotlin/MongoDAOTestJUnit5.kt",
    "content": "package com.phauer.unittestkotlin\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.TestInstance\n\n//Do:\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\nclass MongoDAOTestJUnit5 {\n    private val mongo = KGenericContainer(\"mongo:3.4.3\").apply {\n        withExposedPorts(27017)\n        start()\n    }\n    private val mongoDAO = MongoDAO(host = mongo.containerIpAddress, port = mongo.getMappedPort(27017))\n\n    @Test\n    fun foo() {\n        // test mongoDAO\n    }\n}\n\n//Do:\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\nclass MongoDAOTestJUnit5Constructor {\n    private val mongo: KGenericContainer\n    private val mongoDAO: MongoDAO\n\n    init {\n        mongo = KGenericContainer(\"mongo:3.4.3\").apply {\n            withExposedPorts(27017)\n            start()\n        }\n        mongoDAO = MongoDAO(host = mongo.containerIpAddress, port = mongo.getMappedPort(27017))\n    }\n\n    @Test\n    fun foo() {\n        // test mongoDAO\n    }\n}\n\n//Don't:\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\nclass MongoDAOTestJUnit5BeforeAll {\n    private val mongo: KGenericContainer\n    private val mongoDAO: MongoDAO\n\n    init {\n        mongo = KGenericContainer(\"mongo:3.4.3\").apply {\n            withExposedPorts(27017)\n            start()\n        }\n        mongoDAO = MongoDAO(host = mongo.containerIpAddress, port = mongo.getMappedPort(27017))\n    }\n\n    @Test\n    fun foo() {\n        // test mongoDAO\n    }\n}"
  },
  {
    "path": "unit-tests-kotlin/src/test/kotlin/com/phauer/unittestkotlin/ParseTest.kt",
    "content": "package com.phauer.unittestkotlin\n\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.TestInstance\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.MethodSource\nimport java.util.stream.Stream\n\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\nclass ParseTest {\n\n    @Test\n    fun `parse valid tokens 1`() {\n        assertThat(parse(\"1511443755_2\")).isEqualTo(Token(1511443755, \"2\"))\n        assertThat(parse(\"151175_13521\")).isEqualTo(Token(151175, \"13521\"))\n        assertThat(parse(\"151144375_id\")).isEqualTo(Token(151144375, \"id\"))\n        assertThat(parse(\"15114437599_1\")).isEqualTo(Token(15114437599, \"1\"))\n        assertThat(parse(null)).isEqualTo(null)\n    }\n\n    @Test\n    fun `parse valid tokens 2`() {\n        assertThat(parse(\"1511443755_2\")).isEqualTo(Token(1511443755, \"2\"))\n        assertThat(parse(\"151175_13521\")).isEqualTo(Token(151175, \"13521\"))\n        assertThat(parse(\"151144375_id\")).isEqualTo(Token(151144375, \"id\"))\n        assertThat(parse(\"15114437599_1\")).isEqualTo(Token(15114437599, \"1\"))\n        assertThat(parse(null)).isEqualTo(null)\n    }\n\n    @ParameterizedTest\n    @MethodSource(\"validTokenProvider\")\n    fun `parse valid tokens`(data: TestData) {\n        assertThat(parse(data.input)).isEqualTo(data.expected)\n    }\n\n    private fun validTokenProvider() = Stream.of(\n        TestData(input = \"1511443755_2\", expected = Token(1511443755, \"2\")),\n        TestData(input = \"151175_13521\", expected = Token(151175, \"13521\")),\n        TestData(input = \"151144375_id\", expected = Token(151144375, \"id\")),\n        TestData(input = \"15114437599_1\", expected = Token(15114437599, \"1\")),\n        TestData(input = null, expected = null)\n    )\n}\n\ndata class TestData(\n    val input: String?,\n    val expected: Token?\n)\n\nfun parse(value: String?): Token? {\n    value ?: return null\n    val parts = value.split(\"_\")\n    if (parts.size != 2) {\n        throw IllegalArgumentException(value, null)\n    }\n    try {\n        val timestamp = java.lang.Long.parseUnsignedLong(parts[0])\n        val id = parts[1]\n        return Token(timestamp, id)\n    } catch (ex: Exception) {\n        throw IllegalArgumentException(value, ex)\n    }\n}\n\ndata class Token(\n    val timestamp: Long,\n    val id: String\n)"
  },
  {
    "path": "unit-tests-kotlin/src/test/kotlin/com/phauer/unittestkotlin/ParseTestKotest.kt",
    "content": "package com.phauer.unittestkotlin\n\nimport io.kotest.matchers.shouldBe\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.TestInstance\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.MethodSource\nimport java.util.stream.Stream\n\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\nclass ParseTestKotest {\n\n    @Test\n    fun `parse valid tokens`() {\n        parse(\"1511443755_2\") shouldBe Token(1511443755, \"2\")\n        parse(\"151175_13521\") shouldBe Token(151175, \"13521\")\n        parse(\"151144375_id\") shouldBe Token(151144375, \"id\")\n        parse(\"15114437599_12\") shouldBe Token(15114437599, \"1\")\n        parse(null) shouldBe null\n    }\n\n    @ParameterizedTest\n    @MethodSource(\"validTokenProvider\")\n    fun `parse valid tokens`(data: TestData) {\n        parse(data.input) shouldBe data.expected\n    }\n\n    private fun validTokenProvider() = Stream.of(\n        TestData(input = \"1511443755_2\", expected = Token(1511443755, \"2\")),\n        TestData(input = \"151175_13521\", expected = Token(151175, \"13521\")),\n        TestData(input = \"151144375_id\", expected = Token(151144375, \"id\")),\n        TestData(input = \"15114437599_1\", expected = Token(15114437599, \"1\")),\n        TestData(input = null, expected = null)\n    )\n}\n"
  },
  {
    "path": "unit-tests-kotlin/src/test/kotlin/com/phauer/unittestkotlin/TestSpecificExtFunctions.kt",
    "content": "package com.phauer.unittestkotlin\n\nimport io.kotest.matchers.floats.plusOrMinus\nimport io.kotest.matchers.shouldBe\nimport org.junit.jupiter.api.Test\n\n\nclass TestSpecificExtFunctions {\n\n    // Don't\n    @Test\n    fun bla() {\n        val taxRate1 = 0.3f\n        val taxRate2 = 0.2f\n        val taxRate3 = 0.5f\n\n        taxRate1 shouldBe 0.3f.plusOrMinus(0.001f)\n        taxRate2 shouldBe 0.2f.plusOrMinus(0.001f)\n        taxRate3 shouldBe 0.5f.plusOrMinus(0.001f)\n    }\n\n    // Do\n    @Test\n    fun bla2() {\n        val taxRate1 = 0.3f\n        val taxRate2 = 0.2f\n        val taxRate3 = 0.5f\n\n        taxRate1 shouldBeCloseTo 0.3f\n        taxRate2 shouldBeCloseTo 0.2f\n        taxRate3 shouldBeCloseTo 0.5f\n    }\n\n    private infix fun Float.shouldBeCloseTo(expected: Float) = this shouldBe expected.plusOrMinus(0.001f)\n}"
  },
  {
    "path": "unit-tests-kotlin/src/test/kotlin/com/phauer/unittestkotlin/assertAllOrSomeFields/AssertAllOrSomeFields.kt",
    "content": "package com.phauer.unittestkotlin.assertAllOrSomeFields\n\nimport io.kotest.assertions.asClue\nimport io.kotest.matchers.shouldBe\nimport org.junit.jupiter.api.Test\n\nclass AssertAllOrSomeFields {\n\n    private val dao = DesignDAO()\n\n    @Test\n    fun `all fields are correctly saved`() {\n        // insert design into database\n\n        val expectedDesign = Design(\n            name = \"cat\",\n            userId = 10,\n            tags = listOf(\"Cat\", \"Animal\")\n        )\n        dao.findDesign(1) shouldBe expectedDesign\n    }\n\n    @Test\n    fun `name and tags of a design are changed`() {\n        // change name and tags\n\n        val expectedDesign = Design(\n            name = \"cat\",\n            userId = 10,\n            tags = listOf(\"Cat\", \"Animal\")\n        )\n        dao.findDesign(1) shouldBe expectedDesign\n    }\n\n    @Test\n    fun `name and tags of a design are changed 2`() {\n        // change name and tags\n\n        dao.findDesign(1).asClue {\n            it.name shouldBe \"Cat\"\n            it.userId shouldBe 10\n        }\n    }\n}\n\ndata class Design(\n    val userId: Int,\n    val name: String,\n    val tags: List<String>\n)\n\nclass DesignDAO {\n    fun findDesign(i: Int): Design {\n        TODO()\n    }\n\n}"
  },
  {
    "path": "unit-tests-kotlin/src/test/kotlin/com/phauer/unittestkotlin/foo/CreationHelper.kt",
    "content": "package com.phauer.unittestkotlin.foo\n\nimport java.time.Instant\nimport java.util.Locale\n\ndata class Design(\n    val id: Int,\n    val userId: Int,\n    val name: String,\n    val fileName: String,\n    val dateCreated: Instant,\n    val dateModified: Instant,\n    val tags: Map<Locale, List<Tag>>\n)\n\ndata class Tag(\n    val value: String\n)\n\nfun createDesign(\n    id: Int = 1,\n    name: String = \"Cat\",\n    date: Instant = Instant.ofEpochSecond(1518278198),\n    tags: Map<Locale, List<Tag>> = mapOf(\n        Locale.US to listOf(Tag(value = \"$name in English\")),\n        Locale.GERMANY to listOf(Tag(value = \"$name in German\"))\n    )\n) = Design(\n    id = id,\n    userId = 9,\n    name = name,\n    fileName = name,\n    dateCreated = date,\n    dateModified = date,\n    tags = tags\n)\n\n//usage:\nval testDesign = createDesign()\nval testDesign2 = createDesign(id = 1, name = \"Fox\")\nval testDesign3 = createDesign(id = 1, name = \"Fox\", tags = mapOf())"
  },
  {
    "path": "unit-tests-kotlin/src/test/kotlin/com/phauer/unittestkotlin/foo/MockK.kt",
    "content": "package com.phauer.unittestkotlin.foo\n\nimport com.phauer.unittestkotlin.DesignController\nimport com.phauer.unittestkotlin.DesignDAO\nimport com.phauer.unittestkotlin.DesignMapper\nimport io.mockk.clearMocks\nimport io.mockk.mockk\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.RepeatedTest\nimport org.junit.jupiter.api.TestInstance\n\n//with MockK, the mocked class can be final! no changes required!\n\n// Do:\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\nclass DesignControllerTest_MockK {\n\n    private val dao: DesignDAO = mockk()\n    private val mapper: DesignMapper = mockk()\n    private val controller = DesignController(dao, mapper)\n\n    @BeforeEach\n    fun init() {\n        clearMocks(dao, mapper)\n    }\n\n    // takes 250 ms\n    @RepeatedTest(300)\n    fun foo() {\n        controller.doSomething()\n    }\n}\n\n//Don't\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\nclass DesignControllerTest_RecreatingMocks_MockK {\n\n    private lateinit var dao: DesignDAO\n    private lateinit var mapper: DesignMapper\n    private lateinit var controller: DesignController\n\n    @BeforeEach\n    fun init() {\n        dao = mockk()\n        mapper = mockk()\n        controller = DesignController(dao, mapper)\n    }\n\n    // takes 2 s! (mockk is even slower (0,5 s) than mockito-kotlin)\n    // but this approach is deprecated anyway.\n    @RepeatedTest(300)\n    fun foo() {\n        controller.doSomething()\n    }\n}\n\n//class DesignDAO\n//class DesignMapper\n//class DesignController(val dao: DesignDAO, val mapper: DesignMapper) {\n//    fun doSomething() {\n//\n//    }\n//}\n"
  },
  {
    "path": "unit-tests-kotlin/src/test/kotlin/com/phauer/unittestkotlin/mockk/UserScheduler.kt",
    "content": "package com.phauer.unittestkotlin.mockk\n\n\nclass UserScheduler(\n    val client: UserClient,\n    val dao: UserDAO\n) {\n    fun start(id: Int) {\n        val user = client.getUser(id)\n        dao.saveUser(user)\n    }\n}\n\nclass UserClient {\n    fun getUser(id: Int): User {\n        println(\"getClient()\")\n        return User(id = 99, name = \"Albert\", age = 30)\n    }\n}\n\nclass UserDAO {\n    fun saveUser(user: User) {\n        println(\"saveUser()\")\n    }\n}\n\ndata class User(\n    val id: Int,\n    val name: String,\n    val age: Int\n)\n"
  },
  {
    "path": "unit-tests-kotlin/src/test/kotlin/com/phauer/unittestkotlin/mockk/UserSchedulerTest_MockK.kt",
    "content": "package com.phauer.unittestkotlin.mockk\n\nimport io.mockk.clearAllMocks\nimport io.mockk.every\nimport io.mockk.mockk\nimport io.mockk.verifySequence\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.TestInstance\n\n// Do:\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\nclass UserSchedulerTest {\n\n    private val dao: UserDAO = mockk(relaxed = true)\n    private val client: UserClient = mockk(relaxed = true)\n    private val scheduler = UserScheduler(client, dao)\n\n    @BeforeEach\n    fun init() {\n        clearAllMocks()\n    }\n\n    @Test\n    fun start() {\n        val daoMock: UserDAO = mockk(relaxed = true)\n        val clientMock: UserClient = mockk()\n        val user = User(id = 1, name = \"Ben\", age = 29)\n        every { clientMock.getUser(any()) } returns user\n\n        val scheduler = UserScheduler(clientMock, daoMock)\n        scheduler.start(1)\n\n        verifySequence {\n            clientMock.getUser(1)\n            daoMock.saveUser(user)\n        }\n    }\n\n    @Test\n    fun bla() {\n        val clientMock: UserClient = mockk(relaxed = true)\n        println(clientMock.getUser(1).age) // 0\n\n//        val clientMock2: UserClient = mockk()\n//        println(clientMock2.getUser(1).age) // exception\n    }\n}"
  },
  {
    "path": "unit-tests-kotlin/src/test/resources/mockito-extensions/org.mockito.plugins.MockMakerXXX",
    "content": "mock-maker-inline"
  },
  {
    "path": "uuid-mysql-hibernate/.gitignore",
    "content": ".idea\n*.iml\ntarget/"
  },
  {
    "path": "uuid-mysql-hibernate/README.md",
    "content": "```bash\n# install docker, docker-compose and httpie up front\ndocker-compose up #starts mysql\nmvn package #builds the service\njava -jar target/uuid-mysql-hibernate-1.jar & #start the service\nhttp POST localhost:8080/products name=paul #creates a product. Hibernate generate a UUID\nhttp GET localhost:8080/products #get all products to see the UUIDs\n```"
  },
  {
    "path": "uuid-mysql-hibernate/docker-compose.yml",
    "content": "version: '2'\nservices:\n  mysql:\n    image: mysql:5.7.13\n    ports:\n      - \"3306:3306\"\n    environment:\n     - MYSQL_ROOT_PASSWORD=root #for user 'root'\n     - MYSQL_DATABASE=testdb"
  },
  {
    "path": "uuid-mysql-hibernate/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>de.philipphauer.blog</groupId>\n\t<artifactId>uuid-mysql-hibernate</artifactId>\n\t<version>1</version>\n\t<packaging>jar</packaging>\n\n\t<name>uuid-mysql-hibernate</name>\n\t<description></description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.3.6.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-data-jpa</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>mysql</groupId>\n\t\t\t<artifactId>mysql-connector-java</artifactId>\n\t\t\t<scope>runtime</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "uuid-mysql-hibernate/src/main/java/de/philipphauer/blog/ProductsResource.java",
    "content": "package de.philipphauer.blog;\n\nimport de.philipphauer.blog.model.Product;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.persistence.EntityManager;\nimport javax.persistence.Query;\nimport java.util.Collection;\nimport java.util.Map;\n\n@RestController\n@RequestMapping(value = \"/products\")\npublic class ProductsResource {\n\n    @Autowired\n    private EntityManager entityManager;\n\n    @Transactional\n    @RequestMapping(value = \"\", method = RequestMethod.POST)\n    public void createProduct(@RequestBody Map requestBody) {\n        String name = (String) requestBody.get(\"name\");\n        Product paul = new Product().setName(name);\n        entityManager.persist(paul);\n    }\n\n    @Transactional\n    @RequestMapping(value = \"\", method = RequestMethod.GET)\n    public Collection<Product> getProducts() {\n        Query query = entityManager.createQuery(\"SELECT p FROM Product p\");\n        return (Collection<Product>) query.getResultList();\n    }\n}"
  },
  {
    "path": "uuid-mysql-hibernate/src/main/java/de/philipphauer/blog/UuidMysqlHibernateApplication.java",
    "content": "package de.philipphauer.blog;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class UuidMysqlHibernateApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(UuidMysqlHibernateApplication.class, args);\n\t}\n}\n"
  },
  {
    "path": "uuid-mysql-hibernate/src/main/java/de/philipphauer/blog/model/Product.java",
    "content": "package de.philipphauer.blog.model;\n\nimport org.hibernate.annotations.GenericGenerator;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\nimport java.util.UUID;\n\n@Entity\npublic class Product {\n\n    @Id\n    @GeneratedValue(generator = \"uuid2\")\n    @GenericGenerator(name = \"uuid2\", strategy = \"uuid2\")\n    @Column(columnDefinition = \"BINARY(16)\")\n    private UUID id;\n\n    private String name;\n\n    public UUID getId() {\n        return id;\n    }\n\n    public Product setId(UUID id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public Product setName(String name) {\n        this.name = name;\n        return this;\n    }\n}\n"
  },
  {
    "path": "uuid-mysql-hibernate/src/main/resources/application.properties",
    "content": "spring.datasource.url=jdbc:mysql://localhost:3306/testdb\nspring.datasource.username=root\nspring.datasource.password=root\nspring.datasource.driver-class-name=com.mysql.jdbc.Driver\nspring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect\nspring.jpa.hibernate.ddl-auto = create-drop\nspring.jpa.show-sql = true"
  },
  {
    "path": "vaadin-10-sass-cssrefresh/.gitignore",
    "content": "HELP.md\n/target/\n!.mvn/wrapper/maven-wrapper.jar\n\n### STS ###\n.apt_generated\n.classpath\n.factorypath\n.project\n.settings\n.springBeans\n.sts4-cache\n\n### IntelliJ IDEA ###\n.idea\n*.iws\n*.iml\n*.ipr\n\n### NetBeans ###\n/nbproject/private/\n/nbbuild/\n/dist/\n/nbdist/\n/.nb-gradle/\n/build/\n\n### VS Code ###\n.vscode/\n"
  },
  {
    "path": "vaadin-10-sass-cssrefresh/README.md",
    "content": "# Vaadin 10: SASS Integration and CSS-Refresh Development Workflow\n\n```bash\n# start the app (execute main() in Vaadin10SassCssrefreshApplication)\n\nmvn sass:watch\n\n# open http://localhost:8080/\n\n# change sass\n\n# profit (= css will be updated in the browser without a page reload)\n```\n\nIf you are not using `sass:watch`, mind to execute at least `mvn sass:update-stylesheets` once up front before starting the app. Otherwise there will be no CSS at all."
  },
  {
    "path": "vaadin-10-sass-cssrefresh/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.4.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>com.phauer</groupId>\n    <artifactId>vaadin-10-sass-cssrefresh</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>vaadin-10-sass-cssrefresh</name>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <java.version>11</java.version>\n        <vaadin.version>13.0.3</vaadin.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.vaadin</groupId>\n            <artifactId>vaadin-spring-boot-starter</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>com.vaadin</groupId>\n                <artifactId>vaadin-bom</artifactId>\n                <version>${vaadin.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n            <plugin>\n                <groupId>nl.geodienstencentrum.maven</groupId>\n                <artifactId>sass-maven-plugin</artifactId>\n                <version>3.7.1</version>\n                <configuration>\n                    <resources>\n                        <resource>\n                            <source>\n                                <directory>${basedir}/src/main/resources/META-INF/resources/frontend/styles</directory>\n                                <includes>\n                                    <include>**/*.scss</include>\n                                </includes>\n                            </source>\n                            <relativeOutputDirectory>..</relativeOutputDirectory>\n                            <destination>${basedir}/target/classes/META-INF/resources/frontend/styles\n                            </destination>\n                        </resource>\n                    </resources>\n                </configuration>\n                <executions>\n                    <execution>\n                        <phase>generate-resources</phase>\n                        <goals>\n                            <goal>update-stylesheets</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "vaadin-10-sass-cssrefresh/src/main/java/com/phauer/vaadin10sasscssrefresh/CustomVaadinServiceListener.java",
    "content": "package com.phauer.vaadin10sasscssrefresh;\n\nimport com.vaadin.flow.server.BootstrapListener;\nimport com.vaadin.flow.server.BootstrapPageResponse;\nimport com.vaadin.flow.server.ServiceInitEvent;\nimport com.vaadin.flow.server.VaadinServiceInitListener;\nimport org.jsoup.nodes.Element;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class CustomVaadinServiceListener implements VaadinServiceInitListener {\n    @Override\n    public void serviceInit(ServiceInitEvent event) {\n        if (!event.getSource().getDeploymentConfiguration().isProductionMode()) {\n            event.addBootstrapListener(new CustomBootstrapListener());\n        }\n    }\n    static class CustomBootstrapListener implements BootstrapListener {\n        @Override\n        public void modifyBootstrapPage(BootstrapPageResponse response) {\n            Element head = response.getDocument().head();\n            head.append(\"<script type=\\\"text/javascript\\\" src=\\\"/js/cssrefresh.js\\\"></script>\");\n        }\n    }\n}\n\n"
  },
  {
    "path": "vaadin-10-sass-cssrefresh/src/main/java/com/phauer/vaadin10sasscssrefresh/ExampleView.java",
    "content": "package com.phauer.vaadin10sasscssrefresh;\n\nimport com.vaadin.flow.component.ClickEvent;\nimport com.vaadin.flow.component.button.Button;\nimport com.vaadin.flow.component.html.H1;\nimport com.vaadin.flow.component.html.Label;\nimport com.vaadin.flow.component.orderedlayout.VerticalLayout;\nimport com.vaadin.flow.router.Route;\n\n@Route(value = \"\", layout = MainLayout.class)\npublic class ExampleView extends VerticalLayout {\n\n    public ExampleView(){\n        addClassName(\"exampleView\");\n        add(\n            new H1(\"Example View\"),\n            new Button(\"Do Something\", this::addLabelToView)\n        );\n    }\n\n    private void addLabelToView(ClickEvent<Button> event) {\n        add(new Label(\"The current UI state (this label) is preserved on SASS/CSS changes. No browser refresh is required. No need to click through your application again and again.\"));\n    }\n}\n"
  },
  {
    "path": "vaadin-10-sass-cssrefresh/src/main/java/com/phauer/vaadin10sasscssrefresh/MainLayout.java",
    "content": "package com.phauer.vaadin10sasscssrefresh;\n\nimport com.vaadin.flow.component.dependency.StyleSheet;\nimport com.vaadin.flow.component.html.Div;\nimport com.vaadin.flow.component.page.Push;\nimport com.vaadin.flow.component.page.Viewport;\nimport com.vaadin.flow.router.RouterLayout;\n\n@Push\n@StyleSheet(\"/frontend/styles/main.css\")\n@Viewport(\"width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes\")\npublic class MainLayout extends Div implements RouterLayout {\n    public MainLayout() {\n        setClassName(\"main-layout\");\n    }\n}"
  },
  {
    "path": "vaadin-10-sass-cssrefresh/src/main/java/com/phauer/vaadin10sasscssrefresh/Vaadin10SassCssrefreshApplication.java",
    "content": "package com.phauer.vaadin10sasscssrefresh;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Vaadin10SassCssrefreshApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Vaadin10SassCssrefreshApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "vaadin-10-sass-cssrefresh/src/main/resources/META-INF/resources/frontend/styles/exampleView.scss",
    "content": "@import \"variables\";\n\n.exampleView {\n  h1 {\n    color: $main-color;\n  }\n}"
  },
  {
    "path": "vaadin-10-sass-cssrefresh/src/main/resources/META-INF/resources/frontend/styles/main.scss",
    "content": "@import \"variables\";\n@import \"exampleView\";\n\n\n// vaadin lumo theme adjustments\n:root {\n  --lumo-primary-text-color: #{$main-color};\n  --lumo-primary-color-50pct: rgba(255, 166, 76, 0.5);\n  --lumo-primary-color-10pct: rgba(255, 166, 76, 0.1);\n  --lumo-primary-color: #{$main-color};\n\n  --lumo-error-text-color: #{$error};\n  --lumo-error-color-50pct: rgba(231, 76, 60, 0.5);\n  --lumo-error-color-10pct: rgba(231, 76, 60, 0.1);\n  --lumo-error-color: #{$error};\n\n  --lumo-success-text-color: #{$success};\n  --lumo-success-color-50pct: rgba(39, 174, 96, 0.5);\n  --lumo-success-color-10pct: rgba(39, 174, 96, 0.1);\n  --lumo-success-color: #{$success};\n}\n\n.v-loading-indicator.first {\n  background-color: #ecf0f1;\n}\n\n.v-loading-indicator.second {\n  background-color: #bdc3c7;\n}\n\n.v-loading-indicator.third {\n  background-color: $error;\n}"
  },
  {
    "path": "vaadin-10-sass-cssrefresh/src/main/resources/META-INF/resources/frontend/styles/variables.scss",
    "content": "$main-color: #428bca;\n$error: #e74c3c;\n$success: #27ae60;\n"
  },
  {
    "path": "vaadin-10-sass-cssrefresh/src/main/resources/META-INF/resources/js/cssrefresh.js",
    "content": "(function () {\n    function cssRefresh(link) {\n        let href = link.href + '?x=' + Math.random();\n\n        fetch(href, {\n            method: 'HEAD'\n        })\n            .then(response => {\n                let lastModified = new Date(response.headers.get('last-modified'));\n                if (response.ok && (lastModified > link.lastModified || !link.lastModified)) {\n                    link.lastModified = lastModified;\n                    link.element.setAttribute('href', href);\n                }\n\n                setTimeout(() => {\n                    cssRefresh(link);\n                }, 1000);\n            })\n    }\n\n    Array.prototype.slice.call(document.querySelectorAll('link'))\n        .filter(link => link.rel === 'stylesheet' && link.href)\n        .map(link => {\n            return {\n                element: link,\n                href: link.getAttribute('href').split('?')[0],\n                lastModified: false\n            };\n        })\n        .forEach(function (link) {\n            cssRefresh(link);\n        });\n})();"
  },
  {
    "path": "vaadin-10-sass-cssrefresh/src/main/resources/application.properties",
    "content": "\n"
  },
  {
    "path": "versioning-continuous-delivery/.gitignore",
    "content": ".idea/\n*.iml\ntarget/"
  },
  {
    "path": "versioning-continuous-delivery/README.md",
    "content": "Example configuration to show how to use the git commit hash as the artifact/image version.\n\nAdditionally, I prefix the commit hash with the commit timestamp for better human readability (\"20160702-152019.7555485\").\n\n- `mvn package` to build the jar, wrap it in a docker image and install the image to the local registry.\n- `docker-compose up` to start the built image locally. the service runs on localhost:8080\n- `mvn deploy` to push the image to the docker registry."
  },
  {
    "path": "versioning-continuous-delivery/docker-compose.yml",
    "content": "# image not found? login using \"docker login --username=abc --password=abc https://registry.domain.de\"\nversion: '2'\nservices:\n  versioning-continous-delivery:\n    image: phauer/versioning-continuous-delivery:latest\n    network_mode: \"host\"\n    ports:\n      - \"8080:8080\""
  },
  {
    "path": "versioning-continuous-delivery/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>de.philipphauer.blog</groupId>\n\t<artifactId>versioning-continuous-delivery</artifactId>\n\t<version>1.0.0-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.3.5.RELEASE</version>\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\n\t\t<!-- IntelliJ grouses the following dynamic properties. Workaround: Settings > Build, Execution, Development > Build Tools > Maven > Runner > Properties. Add git.commit.time and git.commit.id.abbrev with empty value. -->\n\t\t<version.number>${git.commit.time}.${git.commit.id.abbrev}</version.number>\n\t\t<docker.repository.name>phauer/${project.artifactId}</docker.repository.name>\n\t\t<!-- Instead of docker hub, you can use your own registry: -->\n\t\t<!--<docker.repository.name>registry.domain.de/group/${project.artifactId}</docker.repository.name>-->\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\n\t\t\t<plugin>\n\t\t\t\t<groupId>pl.project13.maven</groupId>\n\t\t\t\t<artifactId>git-commit-id-plugin</artifactId>\n\t\t\t\t<version>2.2.4</version>\n\t\t\t\t<executions>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<phase>validate</phase>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>revision</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\t\t\t\t<configuration>\n\t\t\t\t\t<dateFormat>yyyyMMdd-HHmmss</dateFormat><!--  human-readable part of the version id -->\n\t\t\t\t\t<dotGitDirectory>${project.basedir}/.git</dotGitDirectory>\n\t\t\t\t\t<generateGitPropertiesFile>false</generateGitPropertiesFile><!-- somehow necessary. otherwise the variables are not available in the pom -->\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\n\t\t\t<plugin>\n\t\t\t\t<groupId>io.fabric8</groupId>\n\t\t\t\t<artifactId>docker-maven-plugin</artifactId>\n\t\t\t\t<version>0.21.0</version>\n\t\t\t\t<extensions>true</extensions>\n\t\t\t\t<configuration>\n                    <!-- a) use \"docker login\" up front to store the credentials on the local machine. -->\n                    <!-- or b) comment in the following snippet: -->\n\t\t\t\t\t<!--<authConfig>-->\n\t\t\t\t\t\t<!--<username>phauer</username>-->\n\t\t\t\t\t\t<!--<password>docker123</password>-->\n\t\t\t\t\t<!--</authConfig>-->\n\t\t\t\t\t<images>\n\t\t\t\t\t\t<image>\n\t\t\t\t\t\t\t<name>${docker.repository.name}:${version.number}</name>\n\t\t\t\t\t\t\t<alias>${project.artifactId}</alias>\n\t\t\t\t\t\t\t<build>\n\t\t\t\t\t\t\t\t<from>openjdk:8-alpine</from>\n\t\t\t\t\t\t\t\t<tags><!-- define additional tags for the image -->\n\t\t\t\t\t\t\t\t\t<tag>latest</tag>\n\t\t\t\t\t\t\t\t</tags>\n\t\t\t\t\t\t\t\t<assembly>\n\t\t\t\t\t\t\t\t\t<descriptorRef>artifact</descriptorRef>\n\t\t\t\t\t\t\t\t</assembly>\n\t\t\t\t\t\t\t\t<ports>\n\t\t\t\t\t\t\t\t\t<port>8080</port>\n\t\t\t\t\t\t\t\t</ports>\n\t\t\t\t\t\t\t\t<cmd>\n\t\t\t\t\t\t\t\t\t<shell>\n\t\t\t\t\t\t\t\t\t\tjava -jar /maven/${project.build.finalName}.jar\n\t\t\t\t\t\t\t\t\t</shell>\n\t\t\t\t\t\t\t\t</cmd>\n\t\t\t\t\t\t\t</build>\n\t\t\t\t\t\t</image>\n\t\t\t\t\t</images>\n\t\t\t\t</configuration>\n\t\t\t\t<executions>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>build-docker-image</id>\n\t\t\t\t\t\t<phase>package</phase>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>build</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t</execution>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>push-docker-image-to-registry</id>\n\t\t\t\t\t\t<phase>deploy</phase>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>push</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\t\t\t</plugin>\n\n\t\t\t<plugin>\n\t\t\t\t<artifactId>maven-install-plugin</artifactId>\n\t\t\t\t<configuration>\n\t\t\t\t\t<skip>true</skip> <!-- we push the container image to the local registry instead -->\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t<artifactId>maven-deploy-plugin</artifactId>\n\t\t\t\t<configuration>\n\t\t\t\t\t<skip>true</skip> <!--  we push the image instead  -->\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n</project>\n"
  },
  {
    "path": "versioning-continuous-delivery/src/main/java/de/philipphauer/blog/VersioningContinuousDeliveryApplication.java",
    "content": "package de.philipphauer.blog;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@SpringBootApplication\n@RestController\npublic class VersioningContinuousDeliveryApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(VersioningContinuousDeliveryApplication.class, args);\n\t}\n\n\t@RequestMapping(\"/\")\n\tpublic String ping() {\n\t\treturn \"Pong\";\n\t}\n}\n"
  },
  {
    "path": "versioning-continuous-delivery/src/main/resources/application.properties",
    "content": ""
  }
]