<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Latte Java</title><link>https://lattejava.org/</link><description>Recent content on Latte Java</description><generator>Hugo</generator><language>en-us</language><atom:link href="https://lattejava.org/index.xml" rel="self" type="application/rss+xml"/><item><title>Getting Started</title><link>https://lattejava.org/cli/docs/getting-started/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/getting-started/</guid><description>&lt;h2 id="prerequisites">Prerequisites&lt;/h2>
&lt;p>Latte requires &lt;strong>Java 25&lt;/strong>. If you don&amp;rsquo;t have it, the easiest way to install it is with &lt;a href="https://lattejava.org/javaenv/">javaenv&lt;/a>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">curl -fsSL https://lattejava.org/javaenv/install &lt;span class="p">|&lt;/span> bash
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">javaenv install &lt;span class="m">25&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="install">Install&lt;/h2>
&lt;p>Getting started with Latte is simple. You can run the install command for *nix systems or WSL2 systems:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">curl -fsSL lattejava.org/cli/install &lt;span class="p">|&lt;/span> bash
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="manual-installation">Manual installation&lt;/h2>
&lt;p>If you prefer not to use the install script, you can install Latte manually by following these steps:&lt;/p>
&lt;h3 id="step-1">Step 1&lt;/h3>
&lt;p>&lt;a href="https://github.com/latte-java/cli/releases">Download the latest version of Latte&lt;/a>&lt;/p></description></item><item><title>Getting Started</title><link>https://lattejava.org/database/docs/getting-started/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/database/docs/getting-started/</guid><description>&lt;h2 id="prerequisites">Prerequisites&lt;/h2>
&lt;p>You need Java 25 and the Latte CLI. If you don&amp;rsquo;t already have them:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">curl -fsSL https://lattejava.org/javaenv/install &lt;span class="p">|&lt;/span> bash
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">javaenv install &lt;span class="m">25&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">javaenv global &lt;span class="m">25&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">curl -fsSL https://lattejava.org/cli/install &lt;span class="p">|&lt;/span> bash
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You also need a JDBC driver for your database. The library itself has no dependencies — it talks to whatever &lt;code>java.sql.Connection&lt;/code> you hand it.&lt;/p>
&lt;h2 id="add-the-dependency">Add the dependency&lt;/h2>
&lt;p>Add &lt;code>database&lt;/code> to the &lt;code>compile&lt;/code> group in your &lt;code>project.latte&lt;/code>, along with the JDBC driver for your database:&lt;/p></description></item><item><title>Getting Started</title><link>https://lattejava.org/json/docs/getting-started/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/json/docs/getting-started/</guid><description>&lt;h2 id="prerequisites">Prerequisites&lt;/h2>
&lt;p>You need Java 25 and the Latte CLI. If you don&amp;rsquo;t already have them:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">curl -fsSL https://lattejava.org/javaenv/install &lt;span class="p">|&lt;/span> bash
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">javaenv install &lt;span class="m">25&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">javaenv global &lt;span class="m">25&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">curl -fsSL https://lattejava.org/cli/install &lt;span class="p">|&lt;/span> bash
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="wire-up-the-processor">Wire up the processor&lt;/h2>
&lt;p>&lt;code>json&lt;/code> is an annotation processor. Latte&amp;rsquo;s Java plugin discovers processors through the &lt;a href="https://lattejava.org/cli/docs/plugin-java/#annotation-processing">&lt;code>compile-processors&lt;/code>&lt;/a> dependency group, so the entire integration is one entry in your &lt;code>project.latte&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-groovy" data-lang="groovy">&lt;span class="line">&lt;span class="cl">&lt;span class="n">dependencies&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">group&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nl">name:&lt;/span> &lt;span class="s2">&amp;#34;compile-processors&amp;#34;&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dependency&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nl">id:&lt;/span> &lt;span class="s2">&amp;#34;org.lattejava:json:0.4.0&amp;#34;&lt;/span>&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Latte hands that artifact to &lt;code>javac&lt;/code> via &lt;code>--processor-module-path&lt;/code>, where the processor is auto-discovered through &lt;code>ServiceLoader&lt;/code> and its &lt;code>@JSON&lt;/code> annotations resolve while your sources compile. The processor runs during both main and test compilation — there&amp;rsquo;s no &lt;code>javac&lt;/code> flag to set, no &lt;code>module-info&lt;/code> &lt;code>provides&lt;/code> clause to write, and no separate codegen target to run.&lt;/p></description></item><item><title>Getting Started</title><link>https://lattejava.org/web/docs/getting-started/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/web/docs/getting-started/</guid><description>&lt;h2 id="prerequisites">Prerequisites&lt;/h2>
&lt;p>You need Java 25 and the Latte CLI. If you don&amp;rsquo;t already have them:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">curl -fsSL https://lattejava.org/javaenv/install &lt;span class="p">|&lt;/span> bash
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">javaenv install &lt;span class="m">25&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">javaenv global &lt;span class="m">25&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">curl -fsSL https://lattejava.org/cli/install &lt;span class="p">|&lt;/span> bash
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="scaffold-a-project">Scaffold a project&lt;/h2>
&lt;p>Create a directory for your project and run &lt;code>latte init&lt;/code> with the &lt;code>web&lt;/code> template:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">mkdir hello-web &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nb">cd&lt;/span> hello-web
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">latte init web
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The CLI prompts for a group, project name (defaults to the directory name), and SPDX license. When it finishes you have a fully wired Latte project that depends on &lt;code>org.lattejava:web&lt;/code>.&lt;/p></description></item><item><title>Create a Project</title><link>https://lattejava.org/cli/docs/create-a-project/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/create-a-project/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>The easiest way to start a new Latte project is with the &lt;code>init&lt;/code> command. This command scaffolds a complete project — a &lt;code>project.latte&lt;/code> file plus the source directories and starter files — from a template.&lt;/p>
&lt;h2 id="running-init">Running init&lt;/h2>
&lt;p>Navigate to the directory you want to turn into a project and run:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ latte init
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>With no arguments, &lt;code>init&lt;/code> uses the built-in &lt;code>library&lt;/code> template. To select a different template, pass its name (or a path) as the first argument:&lt;/p></description></item><item><title>CLI Commands</title><link>https://lattejava.org/cli/docs/cli-commands/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/cli-commands/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>The Latte CLI provides five built-in commands (&lt;code>init&lt;/code>, &lt;code>install&lt;/code>, &lt;code>login&lt;/code>, &lt;code>logout&lt;/code>, and &lt;code>upgrade&lt;/code>) plus a fallback mechanism for running build targets. The syntax is always:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ latte &lt;span class="o">[&lt;/span>switches&lt;span class="o">]&lt;/span> &amp;lt;&lt;span class="nb">command&lt;/span> &lt;span class="p">|&lt;/span> target&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Any argument that is not a built-in command or a flag is treated as a build target name, and Latte looks it up in the current project&amp;rsquo;s &lt;code>project.latte&lt;/code> file. Running &lt;code>latte test&lt;/code> invokes the &lt;code>test&lt;/code> target; running &lt;code>latte clean jar&lt;/code> runs &lt;code>clean&lt;/code> then &lt;code>jar&lt;/code>.&lt;/p></description></item><item><title>CLI Flags</title><link>https://lattejava.org/cli/docs/cli-flags/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/cli-flags/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>Global flags apply to every invocation of &lt;code>latte&lt;/code>, regardless of which command or target you are running. Flags may appear anywhere on the command line — before a command, after a target, or interleaved.&lt;/p>
&lt;p>Any flag that starts with &lt;code>--&lt;/code> and is not one of the flags listed below is treated as a user-defined switch. User-defined switches are made available to your &lt;code>project.latte&lt;/code> file at runtime, allowing you to pass ad-hoc values into a build (for example, &lt;code>latte --env=prod deploy&lt;/code>).&lt;/p></description></item><item><title>Authentication</title><link>https://lattejava.org/cli/docs/authentication/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/authentication/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>Publishing artifacts to the Latte public repository requires authentication. The &lt;code>latte login&lt;/code> command authenticates you with the Latte identity provider and stores the resulting OAuth tokens locally. The &lt;a href="../publishing/">&lt;code>latte()&lt;/code> publish process&lt;/a> then reuses those tokens to publish on your behalf — you never have to manage repository credentials directly.&lt;/p>
&lt;h2 id="logging-in">Logging in&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ latte login
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This command opens your system browser to the Latte login page. If you don&amp;rsquo;t already have a Latte account, you can register for a new account from here.&lt;/p></description></item><item><title>Configuration</title><link>https://lattejava.org/web/docs/configuration/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/web/docs/configuration/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>&lt;code>Configuration&lt;/code> resolves named settings from a layered lookup chain. For each setting name, the sources are consulted in order and the &lt;strong>first defined value wins&lt;/strong>:&lt;/p>
&lt;ol>
&lt;li>The environment variable whose name matches the setting name &lt;strong>verbatim&lt;/strong>.&lt;/li>
&lt;li>The environment variable whose name is the setting name &lt;strong>uppercased&lt;/strong> with every non-alphanumeric character replaced by an underscore (so &lt;code>my-app.some-setting&lt;/code> becomes &lt;code>MY_APP_SOME_SETTING&lt;/code>).&lt;/li>
&lt;li>The Java &lt;strong>system property&lt;/strong> with the same name as the setting.&lt;/li>
&lt;li>Each &lt;strong>properties file&lt;/strong> passed to the constructor that exists on disk, consulted in the order they were supplied.&lt;/li>
&lt;/ol>
&lt;p>Because environment variables and system properties are consulted before files, they override values defined in your properties files — which is what makes per-deployment overrides work without touching the file on disk.&lt;/p></description></item><item><title>Migration Files</title><link>https://lattejava.org/database/docs/migration-files/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/database/docs/migration-files/</guid><description>&lt;h2 id="naming">Naming&lt;/h2>
&lt;p>A migration is a file named &lt;code>&amp;lt;semver&amp;gt;.sql&lt;/code>. The part before the extension is parsed as a strict &lt;a href="https://semver.org">SemVer 2.0.0&lt;/a> version, and that version is the migration&amp;rsquo;s identity — it&amp;rsquo;s what gets recorded, compared, and ordered.&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>File&lt;/th>
 &lt;th>Valid&lt;/th>
 &lt;th>Why&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>1.0.0.sql&lt;/code>&lt;/td>
 &lt;td>Yes&lt;/td>
 &lt;td>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>0.1.0.sql&lt;/code>&lt;/td>
 &lt;td>Yes&lt;/td>
 &lt;td>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>1.36.3.sql&lt;/code>&lt;/td>
 &lt;td>Yes&lt;/td>
 &lt;td>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>2.0.0-beta.1.sql&lt;/code>&lt;/td>
 &lt;td>Yes&lt;/td>
 &lt;td>Pre-release identifiers are allowed&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>1.0.sql&lt;/code>&lt;/td>
 &lt;td>No&lt;/td>
 &lt;td>SemVer requires all three components&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>v1.0.0.sql&lt;/code>&lt;/td>
 &lt;td>No&lt;/td>
 &lt;td>No prefixes&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>01.0.0.sql&lt;/code>&lt;/td>
 &lt;td>No&lt;/td>
 &lt;td>No leading zeros&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>20260725.sql&lt;/code>&lt;/td>
 &lt;td>No&lt;/td>
 &lt;td>Not a SemVer version&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>1.0.0+build.5.sql&lt;/code>&lt;/td>
 &lt;td>No&lt;/td>
 &lt;td>Build metadata is rejected — see below&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Build metadata (the &lt;code>+…&lt;/code> suffix) is rejected deliberately. SemVer says it is ignored when determining precedence, so &lt;code>1.0.0+a.sql&lt;/code> and &lt;code>1.0.0+b.sql&lt;/code> would be two distinct files that tie exactly — there&amp;rsquo;d be no defined order between them and no way to tell which one a recorded version referred to.&lt;/p></description></item><item><title>Project Files</title><link>https://lattejava.org/cli/docs/project-files/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/project-files/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>In this quick start guide to Latte project files we&amp;rsquo;ll cover how to define build dependencies and build targets.&lt;/p>
&lt;h2 id="project-definition">Project definition&lt;/h2>
&lt;p>First, let&amp;rsquo;s set up a simple project file for a new project called &lt;em>HelloWorld&lt;/em>. Here is how we define this project and its version:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-groovy" data-lang="groovy">&lt;span class="line">&lt;span class="cl">&lt;span class="n">project&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nl">group:&lt;/span> &lt;span class="s2">&amp;#34;org.inversoft.latte.example&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nl">name:&lt;/span> &lt;span class="s2">&amp;#34;HelloWorld&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nl">version:&lt;/span> &lt;span class="s2">&amp;#34;1.0&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nl">licenses:&lt;/span> &lt;span class="o">[&lt;/span>&lt;span class="s2">&amp;#34;Apache-2.0&amp;#34;&lt;/span>&lt;span class="o">])&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Project info goes here
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>A project definition needs a group, name, version and a license. You can ignore the licenses definition for now, we&amp;rsquo;ll cover that in a separate quick start guide.&lt;/p></description></item><item><title>Project Layout</title><link>https://lattejava.org/web/docs/project-layout/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/web/docs/project-layout/</guid><description>&lt;h2 id="directory-structure">Directory structure&lt;/h2>
&lt;p>After running &lt;code>latte init web&lt;/code> (with group &lt;code>org.example&lt;/code>, name &lt;code>hello-web&lt;/code>) you&amp;rsquo;ll have a layout like this:&lt;/p>
&lt;pre tabindex="0">&lt;code>hello-web/
├── .gitignore
├── project.latte
├── src/
│ ├── main/
│ │ └── java/
│ │ ├── module-info.java
│ │ └── org/example/hello_web/Main.java
│ └── test/
│ └── java/
│ ├── module-info.java
│ └── org/example/hello_web/tests/MainTest.java
└── web/
 ├── static/
 └── templates/
 └── index.jte
&lt;/code>&lt;/pre>&lt;p>The project is a JPMS module with a &lt;a href="../../../cli/docs/plugin-java/#modules-jpms">separate test module&lt;/a>. The &lt;code>web/static&lt;/code> directory holds static assets (CSS, JavaScript, images) served by the &lt;code>StaticResources&lt;/code> middleware, and &lt;code>web/templates&lt;/code> holds the &lt;a href="../templates/">JTE templates&lt;/a> the app renders. See &lt;a href="../static-files/">Static Files&lt;/a> and &lt;a href="../templates/">Templates&lt;/a> for how they&amp;rsquo;re wired up.&lt;/p></description></item><item><title>Supported Types</title><link>https://lattejava.org/json/docs/supported-types/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/json/docs/supported-types/</guid><description>&lt;p>A &lt;code>@JSON&lt;/code> type&amp;rsquo;s members can be scalars, collections, enums, &lt;code>java.time&lt;/code> types, or other &lt;code>@JSON&lt;/code> types. This page is the authoritative list of what&amp;rsquo;s supported.&lt;/p>
&lt;h2 id="scalars">Scalars&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Java type&lt;/th>
 &lt;th>JSON wire form&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>boolean&lt;/code> / &lt;code>Boolean&lt;/code>&lt;/td>
 &lt;td>&lt;code>true&lt;/code> / &lt;code>false&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>byte&lt;/code>, &lt;code>short&lt;/code>, &lt;code>int&lt;/code>, &lt;code>long&lt;/code> (and boxed)&lt;/td>
 &lt;td>integer&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>float&lt;/code>, &lt;code>double&lt;/code> (and boxed)&lt;/td>
 &lt;td>number&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>BigInteger&lt;/code>&lt;/td>
 &lt;td>integer (unbounded)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>BigDecimal&lt;/code>&lt;/td>
 &lt;td>number (precision preserved)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>String&lt;/code>&lt;/td>
 &lt;td>string&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>UUID&lt;/code>&lt;/td>
 &lt;td>string — canonical &lt;code>8-4-4-4-12&lt;/code> form&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>any &lt;code>enum&lt;/code>&lt;/td>
 &lt;td>string — the constant&amp;rsquo;s &lt;code>name()&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>Instant&lt;/code>, &lt;code>LocalDate&lt;/code>, &lt;code>LocalDateTime&lt;/code>, &lt;code>OffsetDateTime&lt;/code>, &lt;code>ZonedDateTime&lt;/code>, &lt;code>Duration&lt;/code>, &lt;code>Period&lt;/code>&lt;/td>
 &lt;td>string — ISO-8601 (see &lt;a href="../dates-and-times/">Dates &amp;amp; Times&lt;/a>)&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@JSON&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">record&lt;/span> &lt;span class="nc">Bag&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BigInteger&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">big&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">BigDecimal&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">money&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nd">@JSON&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">record&lt;/span> &lt;span class="nc">Ids&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">UUID&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Color&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">color&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// Color is an enum&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Boxed numeric types and reference types accept &lt;code>null&lt;/code>; whether a &lt;code>null&lt;/code> is written depends on &lt;a href="../field-customization/#output-options">&lt;code>@JSON(omitNulls)&lt;/code>&lt;/a>.&lt;/p></description></item><item><title>Project Information</title><link>https://lattejava.org/cli/docs/project-info/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/project-info/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>Latte project files must be placed at the root of the project and they should be named &lt;strong>project.latte&lt;/strong>. Project files will define the project information, dependencies, workflow, and targets.&lt;/p>
&lt;p>Let&amp;rsquo;s start out by looking at a simple Latte project file that defines a project. Edit the file project.latte in your IDE or favorite text editor. Put this in your project file.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-groovy" data-lang="groovy">&lt;span class="line">&lt;span class="cl">&lt;span class="n">project&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nl">group:&lt;/span> &lt;span class="s2">&amp;#34;org.example&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nl">name:&lt;/span> &lt;span class="s2">&amp;#34;my-project&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nl">version:&lt;/span> &lt;span class="s2">&amp;#34;1.0&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nl">licenses:&lt;/span> &lt;span class="o">[&lt;/span>&lt;span class="s2">&amp;#34;Apache-2.0&amp;#34;&lt;/span>&lt;span class="o">])&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// More info here
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You define your project details using the project method. This method takes a block, that allows you to define additional details about your project like dependencies. The key here is that the project requires these attributes:&lt;/p></description></item><item><title>Variables</title><link>https://lattejava.org/cli/docs/variables/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/variables/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>Latte provides a couple of variables you can access from the build script. These are:&lt;/p>
&lt;ul>
&lt;li>&lt;code>SYS&lt;/code> - System properties (e.g. -Dfoo=bar)&lt;/li>
&lt;li>&lt;code>ENV&lt;/code> - Environment variables (e.g. export FOO=BAR)&lt;/li>
&lt;li>&lt;code>switches&lt;/code> - An instance of the &lt;strong>org.lattejava.runtime.Switches&lt;/strong> class that provides access to command-line switches (e.g. &amp;ndash;foo=bar)&lt;/li>
&lt;li>&lt;code>runtimeConfiguration&lt;/code> - The main instance of the &lt;strong>org.lattejava.runtime.RuntimeConfiguration&lt;/strong> for the current Latte runtime&lt;/li>
&lt;li>&lt;code>project&lt;/code> - The main instance of the &lt;strong>org.lattejava.domain.Project&lt;/strong> class that represents the project&lt;/li>
&lt;li>&lt;code>output&lt;/code> - The main instance of the &lt;strong>org.lattejava.output.Output&lt;/strong> class that allows you to print messages&lt;/li>
&lt;li>&lt;code>global&lt;/code> - A global configuration object (see below)&lt;/li>
&lt;/ul>
&lt;p>Here is an example of using these variables:&lt;/p></description></item><item><title>Build Targets</title><link>https://lattejava.org/cli/docs/targets/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/targets/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>Our Latte project file doesn&amp;rsquo;t do anything yet, so let&amp;rsquo;s add some targets.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-groovy" data-lang="groovy">&lt;span class="line">&lt;span class="cl">&lt;span class="n">project&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nl">group:&lt;/span> &lt;span class="s2">&amp;#34;org.example&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nl">name:&lt;/span> &lt;span class="s2">&amp;#34;my-project&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nl">version:&lt;/span> &lt;span class="s2">&amp;#34;1.0&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nl">licenses:&lt;/span> &lt;span class="o">[&lt;/span>&lt;span class="s2">&amp;#34;Apache-2.0&amp;#34;&lt;/span>&lt;span class="o">])&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">target&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nl">name:&lt;/span> &lt;span class="s2">&amp;#34;clean&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nl">description:&lt;/span> &lt;span class="s2">&amp;#34;Cleans out the build directory&amp;#34;&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">target&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nl">name:&lt;/span> &lt;span class="s2">&amp;#34;build&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nl">description:&lt;/span> &lt;span class="s2">&amp;#34;Compiles the project&amp;#34;&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">target&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nl">name:&lt;/span> &lt;span class="s2">&amp;#34;test&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nl">description:&lt;/span> &lt;span class="s2">&amp;#34;Executes the projects tests&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nl">dependsOn:&lt;/span> &lt;span class="o">[&lt;/span>&lt;span class="s2">&amp;#34;build&amp;#34;&lt;/span>&lt;span class="o">])&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is a fairly common project file that includes targets to clean the project, build the project and run the tests. Notice that the &lt;strong>test&lt;/strong> target depends on the &lt;strong>build&lt;/strong> target.&lt;/p>
&lt;h2 id="target-dependencies">Target dependencies&lt;/h2>
&lt;p>Target dependencies are an ordered list. This means that Latte will ensure that dependent targets are executed in the order they are defined in the project file. For example:&lt;/p></description></item><item><title>Dependencies</title><link>https://lattejava.org/cli/docs/dependencies/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/dependencies/</guid><description>&lt;h2 id="installing-dependencies">Installing dependencies&lt;/h2>
&lt;p>The easiest way to add a dependency to your project is with the &lt;code>install&lt;/code> command:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ latte install org.apache.commons:commons-collections:3.1.0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This downloads the artifact from the configured repositories and adds it to the &lt;code>compile&lt;/code> dependency group in your project file.&lt;/p>
&lt;p>To add a dependency to a different group, pass the group name as a second argument:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ latte install org.testng:testng:6.8.7 test-compile
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ latte install javax.servlet:servlet-api:4.0.0 provided
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="manual-dependency-definition">Manual dependency definition&lt;/h2>
&lt;p>You can also add dependencies directly in your project file. Here&amp;rsquo;s an example:&lt;/p></description></item><item><title>Plugins</title><link>https://lattejava.org/cli/docs/plugins-in-build-files/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/plugins-in-build-files/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>At this point, you should have a functional project file that might include targets, dependencies, and a workflow. However, it doesn&amp;rsquo;t do very much. You could start writing your build using Groovy code directly inside the targets. This would require quite a bit of work though. Instead of writing this code yourself, you can leverage plugins to fill out your build.&lt;/p>
&lt;p>We&amp;rsquo;ll assume that your project is a Java project and that you need to compile Java source files. To accomplish this task, we will include the Java plugin and assign it to a variable. This will look like this:&lt;/p></description></item><item><title>Output</title><link>https://lattejava.org/cli/docs/output/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/output/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>No matter where you are in Latte, you have access to the Output object. This object provides methods for outputting information from your build script or plugin. All methods return the Output object, so you can chain calls together.&lt;/p>
&lt;h2 id="methods">Methods&lt;/h2>
&lt;p>Each log level has two variants: one that prints without a trailing newline and one that appends a newline (the &lt;code>ln&lt;/code> suffix).&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>method&lt;/th>
 &lt;th>description&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>info(message, values...)&lt;/code>&lt;/td>
 &lt;td>Prints an informational message&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>infoln(message, values...)&lt;/code>&lt;/td>
 &lt;td>Prints an informational message with a newline&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>infoln(color, message, values...)&lt;/code>&lt;/td>
 &lt;td>Prints a colored informational message with a newline (ANSI 256 color code)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>warning(message, values...)&lt;/code>&lt;/td>
 &lt;td>Prints a warning message&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>warningln(message, values...)&lt;/code>&lt;/td>
 &lt;td>Prints a warning message with a newline&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>error(message, values...)&lt;/code>&lt;/td>
 &lt;td>Prints an error message&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>errorln(message, values...)&lt;/code>&lt;/td>
 &lt;td>Prints an error message with a newline&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>debug(message, values...)&lt;/code>&lt;/td>
 &lt;td>Prints a debug message (only when &lt;code>--debug&lt;/code> is enabled)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>debugln(message, values...)&lt;/code>&lt;/td>
 &lt;td>Prints a debug message with a newline&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>debug(throwable)&lt;/code>&lt;/td>
 &lt;td>Prints a stack trace as a debug message&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>enableDebug()&lt;/code>&lt;/td>
 &lt;td>Enables debug output&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>disableDebug()&lt;/code>&lt;/td>
 &lt;td>Disables debug output&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>All message methods take the message as a String and an optional list of values that are used to fill out the message using &lt;strong>printf&lt;/strong> syntax. For example:&lt;/p></description></item><item><title>Publications</title><link>https://lattejava.org/cli/docs/publications/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/publications/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>Publications are the artifacts produced by your project that can be used by other projects. These are defined in the project file inside the &lt;code>project&lt;/code> definition.&lt;/p>
&lt;p>Any project can have zero or more publications. This means that a single project might produce multiple artifacts that can be used by other projects.&lt;/p>
&lt;p>Publications are split into two groups &lt;strong>main&lt;/strong> and &lt;strong>test&lt;/strong>. Main publications are generally used at runtime and test publications are generally used at test time. Each publication is specified by a &lt;strong>name&lt;/strong>, &lt;strong>type&lt;/strong> and &lt;strong>file&lt;/strong>. Publications can also optionally have a source file specified (i.e. a source JAR). Here&amp;rsquo;s how you define publications:&lt;/p></description></item><item><title>Failing the Build</title><link>https://lattejava.org/cli/docs/failing-the-build/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/failing-the-build/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>If you need to fail a build for any reason, you simply call the fail method from your project file (or plugin) like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-groovy" data-lang="groovy">&lt;span class="line">&lt;span class="cl">&lt;span class="n">fail&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s2">&amp;#34;Something wicked this way comes&amp;#34;&lt;/span>&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Publishing</title><link>https://lattejava.org/cli/docs/publishing/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/publishing/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>The Latte public repository accepts artifacts through an authenticated publish API. Rather than handing build tools long-lived S3/R2 credentials, Latte uses the OAuth tokens from &lt;a href="../authentication/">&lt;code>latte login&lt;/code>&lt;/a> when calling the API.&lt;/p>
&lt;p>You enable this by adding the &lt;code>latte()&lt;/code> process to your project&amp;rsquo;s &lt;code>publishWorkflow&lt;/code>. Projects created with &lt;code>latte init&lt;/code> already include it.&lt;/p>
&lt;h2 id="prerequisite-log-in">Prerequisite: log in&lt;/h2>
&lt;p>Before publishing, you must first login:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ latte login
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If no access token is stored when you publish, the build fails with:&lt;/p></description></item><item><title>Dependency Management</title><link>https://lattejava.org/cli/docs/dependency-management/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/dependency-management/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>At its core, Latte is a dependency management tool. The build system is built on top of this dependency management. If you aren&amp;rsquo;t familiar with dependency management, the simple explanation is that any libraries or frameworks your project uses are its dependencies. Those libraries and frameworks might also have dependencies of their own. This collection of dependencies forms what is called a dependency graph. Latte is really good at managing dependency graphs and downloading and caching your project&amp;rsquo;s dependencies.&lt;/p></description></item><item><title>Records, Classes &amp; Beans</title><link>https://lattejava.org/json/docs/records-classes-and-beans/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/json/docs/records-classes-and-beans/</guid><description>&lt;p>&lt;code>@JSON&lt;/code> can be placed on three kinds of types. All three generate the same companion API (&lt;a href="../getting-started/#serialize-and-deserialize">&lt;code>toJSON&lt;/code> / &lt;code>fromJSON&lt;/code>&lt;/a>); they differ only in how the processor reads values out for serialization and constructs an instance on deserialization.&lt;/p>
&lt;h2 id="records">Records&lt;/h2>
&lt;p>A record is the simplest case: its components are the members, read through the record accessors and written through the canonical constructor. No extra annotations are required.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@JSON&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">record&lt;/span> &lt;span class="nc">User&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">age&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">email&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="classes-with-jsonconstructor">Classes with &lt;code>@JSONConstructor&lt;/code>&lt;/h2>
&lt;p>For an immutable (non-record) class, mark the constructor the deserializer should call with &lt;code>@JSONConstructor&lt;/code>. The constructor&amp;rsquo;s &lt;strong>parameter names&lt;/strong> become the members.&lt;/p></description></item><item><title>Routing</title><link>https://lattejava.org/web/docs/routing/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/web/docs/routing/</guid><description>&lt;h2 id="verbs">Verbs&lt;/h2>
&lt;p>Every standard HTTP verb has a method on &lt;code>Web&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">web&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/users&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">listUsers&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">web&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">post&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/users&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">createUser&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">web&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/users/{id}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">replaceUser&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">web&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">patch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/users/{id}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">updateUser&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">web&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">delete&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/users/{id}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">deleteUser&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">web&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/users&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">corsPreflight&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>To register a handler for several methods at once, use the generic &lt;code>route&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">web&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">route&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">List&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">of&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;GET&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;HEAD&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;/health&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">req&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setStatus&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">200&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>All verb methods return the &lt;code>Web&lt;/code> instance, so you can chain registrations.&lt;/p>
&lt;h2 id="path-parameters">Path parameters&lt;/h2>
&lt;p>Use curly braces for path parameters. The matched value is stored on the request as an attribute keyed by the parameter name:&lt;/p></description></item><item><title>Running Migrations</title><link>https://lattejava.org/database/docs/running-migrations/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/database/docs/running-migrations/</guid><description>&lt;h2 id="the-api">The API&lt;/h2>
&lt;p>There is exactly one entry point:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">List&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Version&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">applied&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Migrator&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">connection&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;db&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="na">migrate&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>migrate()&lt;/code> verifies the checksums of everything already applied, then applies each pending migration in order and returns the versions it applied — in the order they were applied, empty when the database is already up to date. It throws the checked &lt;code>MigrationException&lt;/code> when anything goes wrong, so failures can&amp;rsquo;t be ignored by accident.&lt;/p>
&lt;p>There&amp;rsquo;s no dry-run mode and no separate &amp;ldquo;info&amp;rdquo; or &amp;ldquo;validate&amp;rdquo; call. If you want to know what a run would do, run it: it&amp;rsquo;s idempotent, and the return value tells you what happened.&lt;/p></description></item><item><title>Repositories</title><link>https://lattejava.org/cli/docs/repositories/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/repositories/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>Latte&amp;rsquo;s dependency management system uses repositories to download the artifacts a project depends on. These repositories are usually HTTP web servers.&lt;/p>
&lt;p>The &lt;code>workflow { }&lt;/code> and &lt;code>publishWorkflow { }&lt;/code> DSL shown on this page is covered in full on the &lt;a href="../workflows/">Workflows&lt;/a> page — including the complete list of processes (&lt;code>cache&lt;/code>, &lt;code>mavenCache&lt;/code>, &lt;code>url&lt;/code>, &lt;code>maven&lt;/code>, &lt;code>s3&lt;/code>, and &lt;code>latte&lt;/code>) and their attributes.&lt;/p>
&lt;p>In most commercial environments, project&amp;rsquo;s will use 2 different repositories:&lt;/p>
&lt;ol>
&lt;li>A public repository for all of the open source artifacts&lt;/li>
&lt;li>A internal repository for all of the companies artifacts&lt;/li>
&lt;/ol>
&lt;p>Company artifacts are usually IP and therefore most companies put these artifacts into a secure HTTP server that only their employees have access to.&lt;/p></description></item><item><title>Workflows</title><link>https://lattejava.org/cli/docs/workflows/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/workflows/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>A workflow is an ordered list of processes that Latte runs to resolve dependencies or publish artifacts. When Latte needs to fetch an artifact, it tries each process in order and uses the first one that produces a result. When Latte publishes an artifact, it runs every process in order so the artifact ends up in every configured location.&lt;/p>
&lt;p>Every project file defines two workflows:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;code>workflow { ... }&lt;/code>&lt;/strong> — the fetch-time workflow. Runs whenever Latte needs to download a dependency.&lt;/li>
&lt;li>&lt;strong>&lt;code>publishWorkflow { ... }&lt;/code>&lt;/strong> — the publish-time workflow. Runs whenever Latte publishes a built artifact (for example, during a release).&lt;/li>
&lt;/ul>
&lt;p>The two blocks appear at the top level of the &lt;code>project(...) { }&lt;/code> definition:&lt;/p></description></item><item><title>Field Customization</title><link>https://lattejava.org/json/docs/field-customization/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/json/docs/field-customization/</guid><description>&lt;h2 id="jsonfield">&lt;code>@JSONField&lt;/code>&lt;/h2>
&lt;p>Annotate any record component, &lt;code>@JSONConstructor&lt;/code> parameter, or JavaBean accessor/field with &lt;code>@JSONField&lt;/code> to override how that member is represented. Every element is optional; a member with no &lt;code>@JSONField&lt;/code> uses the type&amp;rsquo;s &lt;a href="../naming-strategies/">naming strategy&lt;/a> and round-trips in both directions.&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Element&lt;/th>
 &lt;th>Type&lt;/th>
 &lt;th>Default&lt;/th>
 &lt;th>Description&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>name&lt;/code>&lt;/td>
 &lt;td>&lt;code>String&lt;/code>&lt;/td>
 &lt;td>&lt;code>&amp;quot;&amp;quot;&lt;/code>&lt;/td>
 &lt;td>Explicit JSON key for this member. Overrides the type&amp;rsquo;s &lt;code>@JSON(naming)&lt;/code> strategy.&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>ignore&lt;/code>&lt;/td>
 &lt;td>&lt;code>boolean&lt;/code>&lt;/td>
 &lt;td>&lt;code>false&lt;/code>&lt;/td>
 &lt;td>Drop the member from both directions — never serialized, never read on input.&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>readOnly&lt;/code>&lt;/td>
 &lt;td>&lt;code>boolean&lt;/code>&lt;/td>
 &lt;td>&lt;code>false&lt;/code>&lt;/td>
 &lt;td>Serialize only. Written to JSON but never populated from input.&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>writeOnly&lt;/code>&lt;/td>
 &lt;td>&lt;code>boolean&lt;/code>&lt;/td>
 &lt;td>&lt;code>false&lt;/code>&lt;/td>
 &lt;td>Deserialize only. Read from input but omitted from the JSON output.&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>format&lt;/code>&lt;/td>
 &lt;td>&lt;code>String&lt;/code>&lt;/td>
 &lt;td>&lt;code>&amp;quot;&amp;quot;&lt;/code>&lt;/td>
 &lt;td>A &lt;code>java.time&lt;/code> pattern for a date/time member — see &lt;a href="../dates-and-times/#custom-patterns-with-format">Dates &amp;amp; Times&lt;/a>.&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>instant&lt;/code>&lt;/td>
 &lt;td>&lt;code>InstantFormat&lt;/code>&lt;/td>
 &lt;td>&lt;code>ISO&lt;/code>&lt;/td>
 &lt;td>Wire representation for an &lt;code>Instant&lt;/code> — see &lt;a href="../dates-and-times/#epoch-integers-with-instant">Dates &amp;amp; Times&lt;/a>.&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@JSON&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">naming&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NamingStrategy&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">SNAKE_CASE&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">record&lt;/span> &lt;span class="nc">Account&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@JSONField&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">accountId&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// → &amp;#34;id&amp;#34; (strategy ignored for this field)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">userName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// → &amp;#34;user_name&amp;#34; (from the strategy)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@JSONField&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">writeOnly&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">password&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// read from input, never serialized&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@JSONField&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">readOnly&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Instant&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">createdAt&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// serialized, never read back&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@JSONField&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ignore&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">scratch&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// absent from JSON entirely&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>A &lt;code>readOnly&lt;/code> (or &lt;code>ignore&lt;/code>) member that &lt;em>does&lt;/em> appear on input is handled like any unknown key: dropped under the default lenient mode, or rejected under &lt;code>@JSON(strict = true)&lt;/code> (see below).&lt;/p></description></item><item><title>Handlers</title><link>https://lattejava.org/web/docs/handlers/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/web/docs/handlers/</guid><description>&lt;h2 id="the-handler-interface">The &lt;code>Handler&lt;/code> interface&lt;/h2>
&lt;p>A handler is a &lt;code>@FunctionalInterface&lt;/code> with a single method:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@FunctionalInterface&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">interface&lt;/span> &lt;span class="nc">Handler&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">handle&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">HTTPRequest&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">req&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">HTTPResponse&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">throws&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Exception&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>That means handlers can be lambdas, method references, or full classes. The simplest possible one returns a status:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">web&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/health&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">req&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setStatus&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">200&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Handlers may throw any exception. Anything not caught by a downstream middleware (such as the &lt;a href="../exception-handling/">&lt;code>ExceptionHandler&lt;/code>&lt;/a>) propagates out and the underlying HTTP server returns a 500.&lt;/p></description></item><item><title>Plugins Overview</title><link>https://lattejava.org/cli/docs/plugins-overview/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/plugins-overview/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>Latte uses a different approach to plugins than most other build tools. Latte&amp;rsquo;s approach is similar to the approach used by Ant. Latte plugins provide reusable logic that project files can leverage. However, plugins do not provide build targets.&lt;/p>
&lt;h1 id="why-no-build-targets">Why no build targets?&lt;/h1>
&lt;p>The decision to move build targets out of plugins and back into the project files was intentional. Build systems that allow plugins to define build targets often run into the issue of plugin dependencies.&lt;/p></description></item><item><title>SQL Scripts</title><link>https://lattejava.org/database/docs/sql-scripts/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/database/docs/sql-scripts/</guid><description>&lt;h2 id="one-file-many-statements">One file, many statements&lt;/h2>
&lt;p>A migration script is executed as a sequence of statements, split on &lt;code>;&lt;/code>. Each statement is sent to the database on its own, in order, over plain JDBC. Statements can be anything the database accepts — DDL, DML, &lt;code>GRANT&lt;/code>, whatever — and no parameter binding is involved: the SQL in the file is the SQL that runs.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">TABLE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">users&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">BIGSERIAL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">PRIMARY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">KEY&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">TEXT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INDEX&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">users_name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">users&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">INSERT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INTO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">users&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">VALUES&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;root&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The final statement doesn&amp;rsquo;t need a trailing semicolon. Whitespace and comments between statements are not sent as statements of their own, so a file that ends with a comment or a stray semicolon is fine.&lt;/p></description></item><item><title>Writing Plugins</title><link>https://lattejava.org/cli/docs/writing-plugins/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/writing-plugins/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>Plugins are the best part about Latte. They are simple and quick to write, simple to test, and easy to start using in your project via integration builds. Let&amp;rsquo;s write a simple plugin that makes a file and puts some text in it.&lt;/p>
&lt;h2 id="project-layout">Project layout&lt;/h2>
&lt;p>First, we need to create the project layout. You don&amp;rsquo;t need to conform to this layout, but this layout works well with the current Latte plugins.&lt;/p></description></item><item><title>Java Plugin</title><link>https://lattejava.org/cli/docs/plugin-java/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/plugin-java/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>The Java plugin allows you to build Java projects. This plugin includes methods for compiling, JARring, and cleaning a Java project.&lt;/p>
&lt;p>&lt;strong>LATEST VERSION: 0.3.0&lt;/strong>&lt;/p>
&lt;h2 id="loading-the-plugin">Loading the plugin&lt;/h2>
&lt;p>Here is how you load this plugin:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-groovy" data-lang="groovy">&lt;span class="line">&lt;span class="cl">&lt;span class="n">java&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">loadPlugin&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nl">id:&lt;/span> &lt;span class="s2">&amp;#34;org.lattejava.plugin:java:0.3.0&amp;#34;&lt;/span>&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="layout">Layout&lt;/h2>
&lt;p>The default layout for a Java project is:&lt;/p>
&lt;pre tabindex="0">&lt;code>project
 |- src/main
 | |- java &amp;lt;- Your main Java source files go here
 | |- resources &amp;lt;- Your main resources (files that are needed at runtime and will be placed into the JAR)
 |
 |- src/test
 | |- java &amp;lt;- Your test Java source files go here
 | |- resources &amp;lt;- Your test resources (files that are needed at test-time and will be placed into the test JAR)
 |
 |- build
 | |- classes
 | | |- main &amp;lt;- The main Java source files are compiled into this directory and the main resources are copied into this directory
 | | |- test &amp;lt;- The test Java source files are compiled into this directory and the test resources are copied into this directory
 | |
 | |- doc &amp;lt;- JavaDoc output from the `document()` method is written here
 | |- jars &amp;lt;- The JAR files that contain your compiled Java files are created here
&lt;/code>&lt;/pre>&lt;p>You can change the layout via the &lt;code>JavaLayout&lt;/code> class. The JavaPlugin class has a member field named &lt;code>layout&lt;/code> that is the main instance of this class. Here&amp;rsquo;s an example:&lt;/p></description></item><item><title>Naming Strategies</title><link>https://lattejava.org/json/docs/naming-strategies/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/json/docs/naming-strategies/</guid><description>&lt;p>The &lt;code>naming&lt;/code> element on &lt;code>@JSON&lt;/code> controls how each member name is converted to its JSON wire key:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@JSON&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">naming&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NamingStrategy&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">SNAKE_CASE&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">record&lt;/span> &lt;span class="nc">Account&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">userName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">httpStatusCode&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// → {&amp;#34;user_name&amp;#34;:&amp;#34;...&amp;#34;,&amp;#34;http_status_code&amp;#34;:&amp;#34;...&amp;#34;}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The converter splits the Java identifier into words — inserting a boundary before each capitalized word and at the tail of an acronym run (&lt;code>userID&lt;/code> → &lt;code>user&lt;/code> + &lt;code>ID&lt;/code>), with digits attaching to the preceding word — then lowercases and rejoins per the strategy.&lt;/p></description></item><item><title>Request Bodies</title><link>https://lattejava.org/web/docs/request-bodies/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/web/docs/request-bodies/</guid><description>&lt;h2 id="the-pattern">The pattern&lt;/h2>
&lt;p>Reading the request body in a handler is two steps: parse it into something useful, then hand it to your handler. Latte web breaks those into two interfaces:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@FunctionalInterface&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">interface&lt;/span> &lt;span class="nc">BodySupplier&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">HTTPRequest&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">req&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">HTTPResponse&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">throws&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Exception&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nd">@FunctionalInterface&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">interface&lt;/span> &lt;span class="nc">BodyHandler&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">handle&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">HTTPRequest&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">req&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">HTTPResponse&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">body&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">throws&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Exception&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You wire them together by registering the route with a body-aware overload:&lt;/p></description></item><item><title>Transactions &amp; Concurrency</title><link>https://lattejava.org/database/docs/transactions-and-concurrency/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/database/docs/transactions-and-concurrency/</guid><description>&lt;h2 id="what-a-run-does">What a run does&lt;/h2>
&lt;p>In order, every call to &lt;code>migrate()&lt;/code>:&lt;/p>
&lt;ol>
&lt;li>Acquires the migration lock — before anything else, including before the version table exists.&lt;/li>
&lt;li>Creates the version table if it isn&amp;rsquo;t there.&lt;/li>
&lt;li>Reads the recorded versions and verifies their checksums against the files.&lt;/li>
&lt;li>Applies each pending migration, in SemVer order, in its own transaction.&lt;/li>
&lt;li>Releases the lock and restores the connection&amp;rsquo;s original &lt;code>autoCommit&lt;/code> setting.&lt;/li>
&lt;/ol>
&lt;p>Step 5 runs on every exit path, including failures. If releasing the lock or restoring the connection fails after a migration already failed, that secondary problem is attached to the original exception as a suppressed exception rather than replacing it — the error you see is always the one that actually broke.&lt;/p></description></item><item><title>Groovy Plugin</title><link>https://lattejava.org/cli/docs/plugin-groovy/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/plugin-groovy/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>The Groovy plugin allows you to build Groovy projects. This plugin includes methods for compiling, jarring, and cleaning a Groovy project.&lt;/p>
&lt;p>&lt;strong>LATEST VERSION: 0.3.0&lt;/strong>&lt;/p>
&lt;h2 id="loading-the-plugin">Loading the plugin&lt;/h2>
&lt;p>Here is how you load this plugin:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-groovy" data-lang="groovy">&lt;span class="line">&lt;span class="cl">&lt;span class="n">groovy&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">loadPlugin&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nl">id:&lt;/span> &lt;span class="s2">&amp;#34;org.lattejava.plugin:groovy:0.3.0&amp;#34;&lt;/span>&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="layout">Layout&lt;/h2>
&lt;p>The default layout for a Groovy project is:&lt;/p>
&lt;pre tabindex="0">&lt;code>project
 |- src/main
 | |- groovy &amp;lt;- Your main Groovy source files go here
 | |- resources &amp;lt;- Your main resources (files that are needed at runtime and will be placed into the JAR)
 |
 |- src/test
 | |- groovy &amp;lt;- Your test Groovy source files go here
 | |- resources &amp;lt;- Your test resources (files that are needed at test-time and will be placed into the test JAR)
 |
 |- build
 | |- classes
 | | |- main &amp;lt;- The main Groovy source files are compiled into this directory and the main resources are copied into this directory
 | | |- test &amp;lt;- The test Groovy source files are compiled into this directory and the test resources are copied into this directory
 | |
 | |- jars &amp;lt;- The JAR files that contain your compiled Groovy files are created here
&lt;/code>&lt;/pre>&lt;p>You can change the layout via the &lt;strong>GroovyLayout&lt;/strong> class. The GroovyPlugin class has a member field named &lt;strong>layout&lt;/strong> that is the main instance of this class. Here&amp;rsquo;s an example:&lt;/p></description></item><item><title>Java TestNG Plugin</title><link>https://lattejava.org/cli/docs/plugin-java-testng/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/plugin-java-testng/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>The Java TestNG plugin allows you to execute TestNG tests in a Java project. It is designed to work alongside the &lt;a href="plugin-java">Java plugin&lt;/a>, which compiles and packages the sources that this plugin runs tests against. The layout, JPMS detection, and source directories described here mirror the Java plugin and share the same defaults.&lt;/p>
&lt;p>&lt;strong>LATEST VERSION: 0.3.0&lt;/strong>&lt;/p>
&lt;h2 id="loading-the-plugin">Loading the plugin&lt;/h2>
&lt;p>Here is how you load this plugin:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-groovy" data-lang="groovy">&lt;span class="line">&lt;span class="cl">&lt;span class="n">javaTestNG&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">loadPlugin&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nl">id:&lt;/span> &lt;span class="s2">&amp;#34;org.lattejava.plugin:java-testng:0.3.0&amp;#34;&lt;/span>&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="settings">Settings&lt;/h2>
&lt;p>The Java TestNG plugin has one required setting, which specifies the Java version used to execute the tests with. The settings for the plugin are configured via the &lt;code>JavaTestNGSettings&lt;/code> class, which is defined on the &lt;code>JavaTestNGPlugin&lt;/code> class via a field named &lt;code>settings&lt;/code>. Here is an example of configuring the plugin:&lt;/p></description></item><item><title>Groovy TestNG Plugin</title><link>https://lattejava.org/cli/docs/plugin-groovy-testng/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/plugin-groovy-testng/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>The Groovy TestNG plugin allows you to execute TestNG tests in a Groovy project.&lt;/p>
&lt;p>&lt;strong>LATEST VERSION: 0.3.0&lt;/strong>&lt;/p>
&lt;h2 id="loading-the-plugin">Loading the plugin&lt;/h2>
&lt;p>Here is how you load this plugin:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-groovy" data-lang="groovy">&lt;span class="line">&lt;span class="cl">&lt;span class="n">groovyTestNG&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">loadPlugin&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nl">id:&lt;/span> &lt;span class="s2">&amp;#34;org.lattejava.plugin:groovy-testng:0.3.0&amp;#34;&lt;/span>&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="settings">Settings&lt;/h2>
&lt;p>The Groovy TestNG plugin requires a couple of configuration settings, including the Groovy version and the Java version to execute the tests with. The settings for the plugin are configured via the &lt;code>GroovyTestNGSettings&lt;/code> class. The &lt;code>GroovyTestNGPlugin&lt;/code> class has a field named &lt;code>settings&lt;/code> that is the main instance of this class. Here is an example of configuring the plugin:&lt;/p></description></item><item><title>File Plugin</title><link>https://lattejava.org/cli/docs/plugin-file/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/plugin-file/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>The file plugin provides a number of methods that help you deal with files and archives.&lt;/p>
&lt;p>&lt;strong>LATEST VERSION: 0.3.0&lt;/strong>&lt;/p>
&lt;h2 id="loading-the-plugin">Loading the plugin&lt;/h2>
&lt;p>Here is how you load this plugin:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-groovy" data-lang="groovy">&lt;span class="line">&lt;span class="cl">&lt;span class="n">file&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">loadPlugin&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nl">id:&lt;/span> &lt;span class="s2">&amp;#34;org.lattejava.plugin:file:0.3.0&amp;#34;&lt;/span>&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="filesets">FileSets&lt;/h2>
&lt;p>Most of the methods on the file plugin use FileSets to identify files. The &lt;code>fileSet&lt;/code> and &lt;code>optionalFileSet&lt;/code> methods allow you to specify files that are either required or optional. If a required FileSet references a &lt;code>dir&lt;/code> that doesn&amp;rsquo;t exist, an exception will be thrown.&lt;/p></description></item><item><title>Cookies</title><link>https://lattejava.org/web/docs/cookies/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/web/docs/cookies/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>&lt;code>Cookies&lt;/code> is an application-facing helper for reading, writing, and clearing cookies. It gives every cookie secure defaults and can optionally encrypt values with AES-256-GCM authenticated encryption. Build one instance at application startup and reuse it across requests and threads — it is immutable and thread-safe.&lt;/p>
&lt;p>There are two factory methods:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nn">module&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">org&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">lattejava&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">web&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// No encryption — plain cookies only&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">var&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">cookies&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Cookies&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">newInstance&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// With encryption keys (enables the .encrypted() variants)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">var&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">cookies&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Cookies&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">encryptionKeys&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="writing-a-cookie">Writing a cookie&lt;/h2>
&lt;p>Start with &lt;code>write(name, value)&lt;/code>, chain any options, then commit with &lt;code>to(req, res)&lt;/code>:&lt;/p></description></item><item><title>Database Plugin</title><link>https://lattejava.org/cli/docs/plugin-database/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/plugin-database/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>The Latte Database Plugin provides the ability to work with databases. It can create databases, execute SQL scripts, apply SQL migrations and compare two schemas.&lt;/p>
&lt;p>&lt;strong>LATEST VERSION: 0.5.0&lt;/strong>&lt;/p>
&lt;h2 id="loading-the-plugin">Loading the plugin&lt;/h2>
&lt;p>Here is how you load this plugin:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-groovy" data-lang="groovy">&lt;span class="line">&lt;span class="cl">&lt;span class="n">database&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">loadPlugin&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nl">id:&lt;/span> &lt;span class="s2">&amp;#34;org.lattejava.plugin:database:0.5.0&amp;#34;&lt;/span>&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="configuring-the-plugin">Configuring the plugin&lt;/h2>
&lt;p>The Database Plugin has a number of configuration options that are stored in the &lt;code>DatabaseSettings&lt;/code> object instance inside the plugin. There are a couple of settings that are required. Other settings have defaults that should work for you, but you can change anything you want.&lt;/p></description></item><item><title>Release Git Plugin</title><link>https://lattejava.org/cli/docs/plugin-release-git/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/plugin-release-git/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>The Release Git plugin allows you to perform a full release of a project stored in a Git repository. Most of what the plugin needs comes from the &lt;code>.git&lt;/code> directory or the project file; it has a single optional setting for customizing the tag name.&lt;/p>
&lt;p>&lt;strong>LATEST VERSION: 0.3.0&lt;/strong>&lt;/p>
&lt;h2 id="loading-the-plugin">Loading the plugin&lt;/h2>
&lt;p>Here is how you load this plugin:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-groovy" data-lang="groovy">&lt;span class="line">&lt;span class="cl">&lt;span class="n">release&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">loadPlugin&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nl">id:&lt;/span> &lt;span class="s2">&amp;#34;org.lattejava.plugin:release-git:0.3.0&amp;#34;&lt;/span>&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="settings">Settings&lt;/h2>
&lt;h3 id="tag-name">Tag name&lt;/h3>
&lt;p>By default, the Git tag created for a release is the project version (e.g. &lt;code>1.0.8&lt;/code>). You can override this with the &lt;code>tag&lt;/code> setting to add a prefix or suffix:&lt;/p></description></item><item><title>Dependency Plugin</title><link>https://lattejava.org/cli/docs/plugin-dependency/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/plugin-dependency/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>The Latte Dependency Management Plugin provides the ability to work with project dependencies.&lt;/p>
&lt;p>&lt;strong>LATEST VERSION: 0.3.0&lt;/strong>&lt;/p>
&lt;h2 id="loading-the-plugin">Loading the plugin&lt;/h2>
&lt;p>Here is how you load this plugin:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-groovy" data-lang="groovy">&lt;span class="line">&lt;span class="cl">&lt;span class="n">dependency&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">loadPlugin&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nl">id:&lt;/span> &lt;span class="s2">&amp;#34;org.lattejava.plugin:dependency:0.3.0&amp;#34;&lt;/span>&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="dependencies">Dependencies&lt;/h2>
&lt;p>All the features of the plugin use a shorthand dependency reference notation. The notation for referencing dependencies looks like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-groovy" data-lang="groovy">&lt;span class="line">&lt;span class="cl">&lt;span class="n">dependencies&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nl">group:&lt;/span> &lt;span class="s2">&amp;#34;compile&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nl">transitive:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nl">fetchSource:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nl">transitiveGroups:&lt;/span> &lt;span class="o">[&lt;/span>&lt;span class="s2">&amp;#34;compile&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="s2">&amp;#34;runtime&amp;#34;&lt;/span>&lt;span class="o">])&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This &lt;code>dependencies&lt;/code> block states that all the project&amp;rsquo;s &lt;code>compile&lt;/code> dependencies should be included AND all the transitive dependencies in the groups &lt;code>compile&lt;/code> and &lt;code>runtime&lt;/code> should also be included. Additionally, the source JAR for each of the dependencies should also be downloaded.&lt;/p></description></item><item><title>Linter Plugin</title><link>https://lattejava.org/cli/docs/plugin-linter/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/plugin-linter/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>The Linter plugin runs static analysis tools on your project&amp;rsquo;s source code. Currently, it supports PMD for Java projects.&lt;/p>
&lt;p>&lt;strong>LATEST VERSION: 0.3.0&lt;/strong>&lt;/p>
&lt;h2 id="loading-the-plugin">Loading the plugin&lt;/h2>
&lt;p>Here is how you load this plugin:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-groovy" data-lang="groovy">&lt;span class="line">&lt;span class="cl">&lt;span class="n">linter&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">loadPlugin&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nl">id:&lt;/span> &lt;span class="s2">&amp;#34;org.lattejava.plugin:linter:0.3.0&amp;#34;&lt;/span>&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="settings">Settings&lt;/h2>
&lt;p>The Linter plugin has one setting for the report output directory:&lt;/p>
&lt;h3 id="report-directory">Report directory&lt;/h3>
&lt;p>The directory where analysis reports are written. This defaults to &lt;code>build/linter-reports&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-groovy" data-lang="groovy">&lt;span class="line">&lt;span class="cl">&lt;span class="n">linter&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">settings&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">reportDirectory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Paths&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">get&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s2">&amp;#34;build/linter-reports&amp;#34;&lt;/span>&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="running-pmd">Running PMD&lt;/h2>
&lt;p>The &lt;code>pmd&lt;/code> method executes PMD analysis on your source code. At minimum, you must specify the rule sets to use:&lt;/p></description></item><item><title>Dates &amp; Times</title><link>https://lattejava.org/json/docs/dates-and-times/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/json/docs/dates-and-times/</guid><description>&lt;p>&lt;code>@JSON&lt;/code> serializes the &lt;code>java.time&lt;/code> types — &lt;code>LocalDate&lt;/code>, &lt;code>LocalDateTime&lt;/code>, &lt;code>OffsetDateTime&lt;/code>, &lt;code>ZonedDateTime&lt;/code>, and &lt;code>Instant&lt;/code> — as ISO-8601 strings by default. Two &lt;code>@JSONField&lt;/code> elements change that per member.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@JSON&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">record&lt;/span> &lt;span class="nc">Times&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Instant&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">instant&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">LocalDate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">localDate&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">OffsetDateTime&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">offsetDateTime&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// → {&amp;#34;instant&amp;#34;:&amp;#34;2026-06-10T17:30:00Z&amp;#34;,&amp;#34;localDate&amp;#34;:&amp;#34;2026-06-10&amp;#34;,&amp;#34;offsetDateTime&amp;#34;:&amp;#34;2026-06-10T17:30:00+01:00&amp;#34;}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="custom-patterns-with-format">Custom patterns with &lt;code>format&lt;/code>&lt;/h2>
&lt;p>&lt;code>format&lt;/code> takes any &lt;code>DateTimeFormatter.ofPattern(...)&lt;/code> string and applies it to &lt;strong>both&lt;/strong> serialization and parsing of that member. It&amp;rsquo;s valid on all five temporal types above.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@JSON&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">record&lt;/span> &lt;span class="nc">Event&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@JSONField&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">format&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;MM/dd/yyyy&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">LocalDate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">day&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// &amp;#34;06/10/2026&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@JSONField&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">format&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;yyyy-MM-dd&amp;#39;T&amp;#39;HH:mm:ss&amp;#39;Z&amp;#39;&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Instant&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// &amp;#34;2026-06-10T17:30:00Z&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The pattern is validated when your code compiles — an invalid pattern (or one containing a &lt;code>&amp;quot;&lt;/code> or &lt;code>\&lt;/code>) fails the build instead of producing broken code or throwing at runtime. For an &lt;code>Instant&lt;/code>, the formatter is anchored to UTC so the pattern can resolve a full date-time.&lt;/p></description></item><item><title>Failures</title><link>https://lattejava.org/database/docs/failures/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/database/docs/failures/</guid><description>&lt;h2 id="exceptions">Exceptions&lt;/h2>
&lt;p>&lt;code>migrate()&lt;/code> throws &lt;code>MigrationException&lt;/code>, a checked exception. &lt;code>ChecksumException&lt;/code> is its one subclass, thrown when the files no longer match what was recorded. Catching &lt;code>MigrationException&lt;/code> catches everything.&lt;/p>
&lt;p>When a failure happens while cleaning up — releasing the lock, restoring the connection — that secondary failure is attached to the original exception with &lt;code>addSuppressed&lt;/code>, so the exception you catch is always the one that describes what actually went wrong. Failures coming from the database carry the driver&amp;rsquo;s &lt;code>SQLException&lt;/code> as their cause.&lt;/p></description></item><item><title>Middleware</title><link>https://lattejava.org/web/docs/middleware/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/web/docs/middleware/</guid><description>&lt;h2 id="the-interface">The interface&lt;/h2>
&lt;p>A middleware is a &lt;code>@FunctionalInterface&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@FunctionalInterface&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">interface&lt;/span> &lt;span class="nc">Middleware&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">handle&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">HTTPRequest&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">req&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">HTTPResponse&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MiddlewareChain&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">chain&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">throws&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Exception&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>It can do three things:&lt;/p>
&lt;ol>
&lt;li>Inspect or mutate the request and response&lt;/li>
&lt;li>Pass control downstream by calling &lt;code>chain.next(req, res)&lt;/code>&lt;/li>
&lt;li>Short-circuit by &lt;strong>not&lt;/strong> calling &lt;code>chain.next&lt;/code> (for example, write a 401 and return)&lt;/li>
&lt;/ol>
&lt;p>Anything you do &lt;strong>after&lt;/strong> &lt;code>chain.next(...)&lt;/code> runs as the chain unwinds — useful for timing, response logging, or post-processing headers.&lt;/p></description></item><item><title>Debian Plugin</title><link>https://lattejava.org/cli/docs/plugin-debian/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/plugin-debian/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>The Latte Debian Package Management Plugin provides the ability to build Debian package files (.deb).&lt;/p>
&lt;p>&lt;strong>LATEST VERSION: 0.3.0&lt;/strong>&lt;/p>
&lt;h2 id="loading-the-plugin">Loading the plugin&lt;/h2>
&lt;p>Here is how you load this plugin:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-groovy" data-lang="groovy">&lt;span class="line">&lt;span class="cl">&lt;span class="n">deb&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">loadPlugin&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nl">id:&lt;/span> &lt;span class="s2">&amp;#34;org.lattejava.plugin:debian:0.3.0&amp;#34;&lt;/span>&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="building-a-debian-package">Building a Debian package&lt;/h2>
&lt;p>The &lt;code>build&lt;/code> method on the plugin allows you to build Debian package files. There are a number of parameters you pass to this method and also a number of closure methods that you can call to control how the Debian package is built. Here are the parameters:&lt;/p></description></item><item><title>Dependency Injection</title><link>https://lattejava.org/web/docs/dependency-injection/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/web/docs/dependency-injection/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>Latte web has no dependency injection container of its own. Instead, &lt;code>Web&lt;/code> accepts an &lt;code>Injector&lt;/code>, a one-method interface that any DI library can satisfy, and uses it to build handlers, controllers, and middleware on demand:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@FunctionalInterface&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">interface&lt;/span> &lt;span class="nc">Injector&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Class&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">type&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Register the injector once, then wrap types with &lt;code>web.inject(...)&lt;/code> wherever a &lt;code>Handler&lt;/code>, &lt;code>BodyHandler&lt;/code>, or &lt;code>Middleware&lt;/code> is expected. The framework calls the injector on every request, so your DI framework controls instance lifetime: a singleton is resolved once and reused, while a prototype is built fresh for each request.&lt;/p></description></item><item><title>IDEA Plugin</title><link>https://lattejava.org/cli/docs/plugin-idea/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/plugin-idea/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>The IDEA plugin allows you to update your IntelliJ IDEA module file (IML) when your project&amp;rsquo;s dependencies change. This plugin does not currently generate IntelliJ IDEA project files (IPR).&lt;/p>
&lt;p>&lt;strong>LATEST VERSION: 0.3.0&lt;/strong>&lt;/p>
&lt;h2 id="loading-the-plugin">Loading the plugin&lt;/h2>
&lt;p>Here is how you load this plugin:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-groovy" data-lang="groovy">&lt;span class="line">&lt;span class="cl">&lt;span class="n">idea&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">loadPlugin&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nl">id:&lt;/span> &lt;span class="s2">&amp;#34;org.lattejava.plugin:idea:0.3.0&amp;#34;&lt;/span>&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="settings">Settings&lt;/h2>
&lt;p>The IDEA plugin provides a couple of settings that help you control how it behaves. None of these settings are required.&lt;/p>
&lt;h3 id="iml-file">IML file&lt;/h3>
&lt;p>The first setting changes the &lt;code>.iml&lt;/code> that the plugin updates. Some projects have different names than the &lt;code>.iml&lt;/code> file and this setting allows you to configure the file to use. Here is an example:&lt;/p></description></item><item><title>POM Plugin</title><link>https://lattejava.org/cli/docs/plugin-pom/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/plugin-pom/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>The POM plugin generates and updates Maven &lt;code>pom.xml&lt;/code> files from your Latte project&amp;rsquo;s dependencies. This is useful when you need to publish your project to a Maven repository or provide Maven compatibility.&lt;/p>
&lt;p>&lt;strong>LATEST VERSION: 0.3.0&lt;/strong>&lt;/p>
&lt;h2 id="loading-the-plugin">Loading the plugin&lt;/h2>
&lt;p>Here is how you load this plugin:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-groovy" data-lang="groovy">&lt;span class="line">&lt;span class="cl">&lt;span class="n">pom&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">loadPlugin&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nl">id:&lt;/span> &lt;span class="s2">&amp;#34;org.lattejava.plugin:pom:0.3.0&amp;#34;&lt;/span>&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="settings">Settings&lt;/h2>
&lt;p>The POM plugin has one main setting that controls how Latte dependency groups are mapped to Maven scopes. The settings for the plugin are configured via the &lt;code>POMSettings&lt;/code> class. The &lt;code>POMPlugin&lt;/code> class has a field named &lt;code>settings&lt;/code> that is the main instance of this class.&lt;/p></description></item><item><title>Logging</title><link>https://lattejava.org/web/docs/logging/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/web/docs/logging/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>The web and underlying HTTP layers log through a swappable &lt;code>LoggerFactory&lt;/code>. When you don&amp;rsquo;t configure one, &lt;code>Web&lt;/code> uses &lt;code>WebPrintStreamLoggerFactory.FACTORY&lt;/code> by default. That factory always returns a single shared &lt;code>WebPrintStreamLogger&lt;/code> that writes to &lt;code>System.out&lt;/code>, prefixing each line with an ISO-8601 offset date-time formatted in the system default time zone (e.g. &lt;code>2026-04-27T13:45:23.689-04:00&lt;/code>).&lt;/p>
&lt;p>Once the server is up, &lt;code>Web&lt;/code> logs a single line announcing where the application is reachable:&lt;/p>
&lt;pre tabindex="0">&lt;code>Web application is available at [http://localhost:8080]
&lt;/code>&lt;/pre>&lt;p>The URL is built from the bind address and port, so it points at &lt;code>localhost&lt;/code> when bound to any-local, or the actual host/port otherwise.&lt;/p></description></item><item><title>Catch-All Members</title><link>https://lattejava.org/json/docs/catch-all/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/json/docs/catch-all/</guid><description>&lt;p>&lt;code>@JSONCatchAll&lt;/code> marks one member as the bucket for any JSON keys that don&amp;rsquo;t map to a declared member. Instead of dropping unknown keys (lenient mode) or throwing on them (&lt;a href="../field-customization/#output-options">&lt;code>strict&lt;/code> mode&lt;/a>), the parser collects them into a map — and writes them back out on serialize.&lt;/p>
&lt;p>The annotated member must be typed exactly &lt;code>Map&amp;lt;String, Object&amp;gt;&lt;/code>, and a type may have at most one catch-all.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@JSON&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">record&lt;/span> &lt;span class="nc">Response&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">code&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nd">@JSONCatchAll&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Map&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Object&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">extras&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The &lt;code>@JSONCatchAll&lt;/code> annotation is what makes this a catch-all. A &lt;code>Map&amp;lt;String, Object&amp;gt;&lt;/code> member &lt;strong>without&lt;/strong> it is a &lt;a href="../supported-types/#dynamic-objects">dynamic object&lt;/a> — captured under its own key rather than spread across the top level. A type can have both: the catch-all collects leftover unknown keys while each dynamic-object member captures its own.&lt;/p></description></item><item><title>Static Files</title><link>https://lattejava.org/web/docs/static-files/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/web/docs/static-files/</guid><description>&lt;h2 id="the-shorthand">The shorthand&lt;/h2>
&lt;p>The fastest way to serve static files is &lt;code>Web.files(urlPrefix)&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">web&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">files&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/assets&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>That installs a &lt;code>StaticResources&lt;/code> middleware that maps &lt;code>/assets/*&lt;/code> to &lt;code>&amp;lt;baseDir&amp;gt;/assets/*&lt;/code> on disk. The base directory defaults to the current working directory; override it with &lt;code>Web.baseDir(Path)&lt;/code> if needed:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">web&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">baseDir&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Paths&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/var/www&amp;#34;&lt;/span>&lt;span class="p">)).&lt;/span>&lt;span class="na">files&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/assets&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If the URL prefix and the on-disk subdirectory don&amp;rsquo;t match, pass both:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">web&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">files&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/assets&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;public&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// /assets/app.css -&amp;gt; &amp;lt;baseDir&amp;gt;/public/app.css&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="the-middleware">The middleware&lt;/h2>
&lt;p>Under the hood, &lt;code>files&lt;/code> installs a &lt;code>StaticResources&lt;/code> middleware. Use the constructor directly when you need control over caching or want to filter requests:&lt;/p></description></item><item><title>Templates</title><link>https://lattejava.org/web/docs/templates/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/web/docs/templates/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>&lt;code>JTETemplates&lt;/code> (in &lt;code>org.lattejava.web.jte&lt;/code>) renders &lt;a href="https://jte.gg/">JTE&lt;/a> templates. The &lt;code>web&lt;/code> library depends on &lt;code>gg.jte:jte&lt;/code>, so JTE is available to any project that uses Latte Web. Projects created with &lt;code>latte init web&lt;/code> include a starter &lt;code>web/templates/index.jte&lt;/code>.&lt;/p>
&lt;p>There are two ways to use it:&lt;/p>
&lt;ul>
&lt;li>The &lt;code>html(...)&lt;/code> methods write the rendered HTML straight to the &lt;code>HTTPResponse&lt;/code>. They set the content type to &lt;code>text/html; charset=utf-8&lt;/code> and leave the status alone (so if something upstream set a status, it is preserved; otherwise it defaults to 200).&lt;/li>
&lt;li>The &lt;code>render(...)&lt;/code> methods return the rendered output as a &lt;code>String&lt;/code>. Use these for non-HTTP scenarios such as composing email bodies or building reusable fragments.&lt;/li>
&lt;/ul>
&lt;p>In every template the &lt;code>request&lt;/code> and &lt;code>response&lt;/code> objects are available as the &lt;code>request&lt;/code> and &lt;code>response&lt;/code> parameters.&lt;/p></description></item><item><title>Flash Messages</title><link>https://lattejava.org/web/docs/flash-messages/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/web/docs/flash-messages/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>A handler that processes a form usually redirects afterward (POST, redirect, GET). Any message it wants to show, such as &amp;ldquo;Settings saved&amp;rdquo;, has to outlive the redirect and reach the page that is finally rendered. Flash messages solve this with two pieces:&lt;/p>
&lt;ul>
&lt;li>&lt;code>Flash&lt;/code> (in &lt;code>org.lattejava.web&lt;/code>) is a thin wrapper around the request. Handlers use it to add messages and templates use it to read them.&lt;/li>
&lt;li>&lt;code>FlashMessages&lt;/code> (in &lt;code>org.lattejava.web.middleware&lt;/code>) is the middleware that carries the messages between requests in a cookie named &lt;code>flash&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>Install the middleware globally, then use &lt;code>Flash&lt;/code> anywhere the request is available:&lt;/p></description></item><item><title>Message Bundles</title><link>https://lattejava.org/web/docs/message-bundles/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/web/docs/message-bundles/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>&lt;code>Messages&lt;/code> (in &lt;code>org.lattejava.web&lt;/code>) looks up display text for the current request from &lt;code>.properties&lt;/code> files under &lt;code>web/messages&lt;/code> in the project&amp;rsquo;s base directory. The files are arranged in directories that match the path of the request. This allows the templates and message bundles to mirror each other on disk.&lt;/p>
&lt;p>Message bundles can also be localized in the same manner that &lt;code>ResourceBundle&lt;/code>s are localized. The lookup chain is the same using the different parts of the &lt;code>Locale&lt;/code>. See the information below for the lookup chain.&lt;/p></description></item><item><title>Polymorphism</title><link>https://lattejava.org/json/docs/polymorphism/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/json/docs/polymorphism/</guid><description>&lt;p>A sealed &lt;code>@JSON&lt;/code> interface can round-trip its permitted subtypes through a &lt;strong>discriminator&lt;/strong> — a JSON key whose value names the concrete type. Annotate the sealed interface with &lt;code>@JSONTypeInfo&lt;/code> to declare the discriminator key, and each permitted subtype with &lt;code>@JSONSubtype&lt;/code> to declare its value.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@JSON&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nd">@JSONTypeInfo&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">property&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;petType&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">sealed&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">interface&lt;/span> &lt;span class="nc">Pet&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">permits&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Dog&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Cat&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Bird&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nd">@JSON&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">record&lt;/span> &lt;span class="nc">Dog&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">packSize&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">implements&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Pet&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nd">@JSON&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nd">@JSONSubtype&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;kitty&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">record&lt;/span> &lt;span class="nc">Cat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">lives&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">implements&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Pet&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nd">@JSON&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nd">@JSONSubtype&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Bird&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">record&lt;/span> &lt;span class="nc">Bird&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">implements&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Pet&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The companion generated for the sealed type (&lt;code>PetJSON&lt;/code>) has the same &lt;a href="../getting-started/#serialize-and-deserialize">&lt;code>toJSON&lt;/code> / &lt;code>fromJSON&lt;/code> API&lt;/a> as any other type, but it dispatches on the discriminator:&lt;/p></description></item><item><title>Security Headers</title><link>https://lattejava.org/web/docs/security-headers/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/web/docs/security-headers/</guid><description>&lt;h2 id="why">Why&lt;/h2>
&lt;p>Browsers respect a handful of HTTP response headers that mitigate clickjacking, MIME sniffing, mixed content, and a long tail of other attacks. The right defaults are well understood, so the framework ships them as a single middleware: &lt;code>SecurityHeaders&lt;/code>.&lt;/p>
&lt;h2 id="defaults">Defaults&lt;/h2>
&lt;p>&lt;code>SecurityHeaders&lt;/code> is immutable. Start from one of two factory methods:&lt;/p>
&lt;ul>
&lt;li>&lt;code>SecurityHeaders.defaults()&lt;/code> — every header set to its most-secure value.&lt;/li>
&lt;li>&lt;code>SecurityHeaders.empty()&lt;/code> — no headers; opt in to the ones you want.&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nn">module&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">org&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">lattejava&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">web&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">web&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">install&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SecurityHeaders&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">defaults&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Each header is written &lt;strong>only if the response does not already have it&lt;/strong> (&lt;code>getHeader(name) == null&lt;/code>), so a handler or an upstream middleware can override any header simply by setting it first. The headers appear on every response that runs through the chain — including 404s and 5xx errors.&lt;/p></description></item><item><title>CSRF Protection</title><link>https://lattejava.org/web/docs/csrf-protection/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/web/docs/csrf-protection/</guid><description>&lt;h2 id="the-strategy">The strategy&lt;/h2>
&lt;p>Latte web uses two layers to defend against cross-site request forgery:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>&lt;code>SameSite=Strict&lt;/code> cookies&lt;/strong> — the underlying HTTP server sets session cookies with &lt;code>SameSite=Strict&lt;/code> so browsers won&amp;rsquo;t include them on cross-origin requests in the first place&lt;/li>
&lt;li>&lt;strong>&lt;code>Origin&lt;/code> header validation&lt;/strong> — the &lt;code>OriginChecks&lt;/code> middleware rejects unsafe-method requests whose &lt;code>Origin&lt;/code> header doesn&amp;rsquo;t match the server&lt;/li>
&lt;/ol>
&lt;p>There are no double-submit tokens or hidden form fields to manage. The cookies and the &lt;code>Origin&lt;/code> header do the work together.&lt;/p></description></item><item><title>Performance</title><link>https://lattejava.org/json/docs/performance/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/json/docs/performance/</guid><description>&lt;p>Because the serializer and deserializer are generated at compile time, there&amp;rsquo;s no reflection, no per-call type introspection, and no schema cache to warm up — the companion is straight-line Java that reads and writes fields directly. The result is throughput at or above Jackson&amp;rsquo;s, with markedly lower allocation, and a comfortable lead over Gson.&lt;/p>
&lt;h2 id="benchmarks">Benchmarks&lt;/h2>
&lt;p>Measured with JMH against Jackson databind and Gson across six workloads, in both directions. Higher ops/sec is better; lower allocation (bytes per operation, in parentheses) is better.&lt;/p></description></item><item><title>Releasing</title><link>https://lattejava.org/cli/docs/releasing/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/cli/docs/releasing/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>Releasing a project involves a number of steps, depending on the type of project and how the project is version controlled. For this example, we will assume that the project is a Java project and managed in Git. For this, we will use the &lt;a href="https://lattejava.org/cli/docs/plugin-release-git/">Release Git Plugin&lt;/a>.&lt;/p>
&lt;p>The steps required to release a Java/Git project are:&lt;/p>
&lt;ol>
&lt;li>Check for dependencies on integration builds of other projects, libraries, etc.&lt;/li>
&lt;li>Check for plugins that are integration builds&lt;/li>
&lt;li>Ensure the project is a Git project&lt;/li>
&lt;li>Perform a &lt;strong>git pull&lt;/strong>&lt;/li>
&lt;li>Ensure the project has no local changes&lt;/li>
&lt;li>Ensure the project changes have been pushed to the remote&lt;/li>
&lt;li>Ensure there isn&amp;rsquo;t a tag in the Git repository for the version being released&lt;/li>
&lt;li>Creates a tag whose name is the version being released (i.e. 1.0.8)&lt;/li>
&lt;li>Publishes the project&amp;rsquo;s artifacts (publications) using the publishWorkflow of the project&lt;/li>
&lt;/ol>
&lt;p>Luckily, the &lt;a href="https://lattejava.org/cli/docs/plugin-release-git/">Release Git Plugin&lt;/a> does most of the work. All you need to do is add a few things to your project file and you will be able to release projects quickly and easily.&lt;/p></description></item><item><title>Exception Handling</title><link>https://lattejava.org/web/docs/exception-handling/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/web/docs/exception-handling/</guid><description>&lt;h2 id="the-pattern">The pattern&lt;/h2>
&lt;p>Throw exceptions where your code wants to bail out, and let an &lt;code>ExceptionHandler&lt;/code> middleware turn them into HTTP responses. Each exception type is mapped to an &lt;code>ErrorRenderer&lt;/code> — a function that owns the entire response for that exception: it sets the status code &lt;strong>and&lt;/strong> writes the body.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nn">module&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">java&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">base&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kn">import&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nn">module&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">org&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">lattejava&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">web&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">NoteNotFoundException&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">extends&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RuntimeException&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">var&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">errors&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ExceptionHandler&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Map&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">of&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">NoteNotFoundException&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">req&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setStatus&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">404&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getWriter&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;{\&amp;#34;error\&amp;#34;:\&amp;#34;not found\&amp;#34;}&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Web&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">install&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">errors&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/notes/{id}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">req&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// ... look up the note, throw NoteNotFoundException if missing ...&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">})&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">start&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">8080&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>ErrorRenderer&lt;/code> is a functional interface:&lt;/p></description></item><item><title>OIDC Authentication</title><link>https://lattejava.org/web/docs/oidc/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/web/docs/oidc/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>The &lt;code>org.lattejava.web.oidc&lt;/code> package provides drop-in OpenID Connect authentication. It works against any standards-compliant OIDC provider, but we recommend &lt;a href="https://fusionauth.io/">FusionAuth&lt;/a> — it runs locally during development, deploys self-hosted in production, and is what &lt;code>web&lt;/code> itself runs against in its own OIDC integration test suite. Keycloak, Auth0, Okta, Google, and other compliant providers also work.&lt;/p>
&lt;p>OIDC supports three application styles, which differ along two axes — how tokens travel (cookies vs. &lt;code>Authorization&lt;/code> headers) and how the framework responds when a request is not authenticated (an HTML redirect vs. a status code):&lt;/p></description></item><item><title>Testing</title><link>https://lattejava.org/web/docs/testing/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/web/docs/testing/</guid><description>&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>The &lt;code>org.lattejava.web.test&lt;/code> package gives you two things for testing a Latte Web application:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;code>WebTest&lt;/code>&lt;/strong> — a fluent HTTP client that issues real requests against a &lt;code>Web&lt;/code> instance running on a port and returns an asserter for the response.&lt;/li>
&lt;li>&lt;strong>&lt;code>OIDCTestFixture&lt;/code>&lt;/strong> — a helper that walks the full OAuth2 authorization-code + PKCE flow against a real IdP so your tests can run as an authenticated user.&lt;/li>
&lt;/ul>
&lt;p>Because &lt;code>WebTest&lt;/code> makes real HTTP calls over &lt;code>localhost&lt;/code>, you start your &lt;code>Web&lt;/code> app exactly as you would in production, then point a &lt;code>WebTest&lt;/code> at the same port. There is no mock layer — what you test is what runs.&lt;/p></description></item><item><title>Sample Application</title><link>https://lattejava.org/web/docs/sample-application/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/web/docs/sample-application/</guid><description>&lt;h2 id="what-it-does">What it does&lt;/h2>
&lt;p>This sample wires together:&lt;/p>
&lt;ul>
&lt;li>A public landing page&lt;/li>
&lt;li>Static asset serving&lt;/li>
&lt;li>An OIDC-protected &lt;code>/app&lt;/code> section&lt;/li>
&lt;li>A JSON API behind authentication and a role check&lt;/li>
&lt;li>Security headers, CSRF defense, and exception rendering&lt;/li>
&lt;/ul>
&lt;p>It&amp;rsquo;s everything in the docs in one file.&lt;/p>
&lt;h2 id="mainjava">&lt;code>Main.java&lt;/code>&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nn">module&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">com&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">fasterxml&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">jackson&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">databind&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kn">import&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nn">module&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">org&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">lattejava&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">http&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kn">import&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nn">module&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">org&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">lattejava&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">jwt&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kn">import&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nn">module&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">org&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">lattejava&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">web&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// --- OIDC -----------------------------------------------------------------&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">var&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">oidcConfig&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">OIDCConfig&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">builder&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">issuer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;https://auth.example.com&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">clientId&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;notes-app&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">clientSecret&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">System&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getenv&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;OIDC_SECRET&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">build&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">var&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">browser&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">BrowserSettings&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">builder&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">postLoginPage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/app&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">postLogoutPage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">build&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">var&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">oidc&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">OIDC&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">ssr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">oidcConfig&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">browser&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">jwt&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">jwt&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// --- Exception -&amp;gt; response ------------------------------------------------&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">var&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">errors&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ExceptionHandler&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Map&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">of&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">NoteNotFoundException&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">req&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setStatus&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">404&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setContentType&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;application/json&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getWriter&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;{\&amp;#34;error\&amp;#34;:\&amp;#34;note not found\&amp;#34;}&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">var&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">web&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Web&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">web&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">baseDir&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Paths&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;web&amp;#34;&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// --- Global middlewares -------------------------------------------------&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">web&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">install&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SecurityHeaders&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">defaults&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">web&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">install&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">OriginChecks&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">web&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">install&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">errors&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">web&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">install&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">OIDC&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">sessionEndpoints&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">oidcConfig&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">browser&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// --- Static assets ------------------------------------------------------&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">web&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">files&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/static&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// --- Public routes ------------------------------------------------------&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">web&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">_&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setContentType&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;text/html; charset=utf-8&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getWriter&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> &amp;lt;!doctype html&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> &amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;Notes&amp;lt;/title&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> &amp;lt;link rel=&amp;#34;stylesheet&amp;#34; href=&amp;#34;/static/app.css&amp;#34;&amp;gt;&amp;lt;/head&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> &amp;lt;body&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> &amp;lt;h1&amp;gt;Notes&amp;lt;/h1&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> &amp;lt;a href=&amp;#34;/login&amp;#34;&amp;gt;Sign in&amp;lt;/a&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> &amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">});&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">web&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/health&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">_&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setStatus&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">200&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// --- Authenticated app --------------------------------------------------&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">web&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">prefix&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/app&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">app&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">app&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">install&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">oidc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">authenticated&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">app&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">_&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">JWT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">jwt&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">OIDC&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">jwt&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setContentType&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;text/html; charset=utf-8&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getWriter&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;&amp;lt;h1&amp;gt;Welcome, &amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">jwt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">subject&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;&amp;lt;/h1&amp;gt;&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">});&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">});&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// --- Authenticated JSON API --------------------------------------------&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">web&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">prefix&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/api&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">api&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">api&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">install&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">oidc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">authenticated&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">api&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/notes/{id}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">req&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">req&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getAttribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// throws NoteNotFoundException -&amp;gt; 404 via ExceptionHandler&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Note&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">note&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NoteStore&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">find&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">writeJson&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">200&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">note&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">});&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">api&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">post&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/notes&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">_&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">note&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">JWT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">jwt&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">OIDC&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">jwt&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Note&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">created&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NoteStore&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">create&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">jwt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">subject&lt;/span>&lt;span class="p">(),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">note&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">writeJson&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">201&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">created&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">},&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">JSONBodySupplier&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">of&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">NewNote&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">oidc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">hasAnyRole&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;editor&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;admin&amp;#34;&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">api&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">delete&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/notes/{id}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">req&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">req&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getAttribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">NoteStore&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">delete&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setStatus&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">204&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">},&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">oidc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">hasAllRoles&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;admin&amp;#34;&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">});&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">web&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">start&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">8080&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">System&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">out&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">println&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Listening on http://localhost:8080&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">writeJson&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">HTTPResponse&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">status&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Object&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">body&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">throws&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Exception&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setStatus&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">status&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setContentType&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;application/json&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getWriter&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ObjectMapper&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">writeValueAsString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">body&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">record&lt;/span> &lt;span class="nc">NewNote&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">title&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">body&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">record&lt;/span> &lt;span class="nc">Note&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">owner&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">title&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">body&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">NoteNotFoundException&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">extends&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RuntimeException&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">NoteStore&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Map&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Note&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">store&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ConcurrentHashMap&lt;/span>&lt;span class="o">&amp;lt;&amp;gt;&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Note&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">create&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">owner&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NewNote&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">input&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">UUID&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">randomUUID&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">toString&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Note&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Note&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">owner&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">input&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">title&lt;/span>&lt;span class="p">(),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">input&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">body&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">store&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">delete&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">store&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">remove&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Note&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">find&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Note&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">store&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">throw&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NoteNotFoundException&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="whats-happening">What&amp;rsquo;s happening&lt;/h2>
&lt;p>A few details worth calling out:&lt;/p></description></item><item><title>Why Latte?</title><link>https://lattejava.org/why.html</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lattejava.org/why.html</guid><description/></item></channel></rss>