<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Nikolay Stanchev's blog]]></title><description><![CDATA[Nikolay Stanchev's blog]]></description><link>https://blog.teonibyte.com</link><generator>RSS for Node</generator><lastBuildDate>Tue, 28 Apr 2026 09:13:07 GMT</lastBuildDate><atom:link href="https://blog.teonibyte.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Introducing Lambdora – A Tool for Time-Traveling Through Your Data]]></title><description><![CDATA[Overview
The purpose of this post is to introduce Lambdora - a side project I’ve been working on that turns change data capture (CDC) events into a clear, queryable timeline of how data evolves over time.
Lambdora takes raw CDC streams and transforms...]]></description><link>https://blog.teonibyte.com/introducing-lambdora-a-tool-for-time-traveling-through-your-data</link><guid isPermaLink="true">https://blog.teonibyte.com/introducing-lambdora-a-tool-for-time-traveling-through-your-data</guid><dc:creator><![CDATA[Nikolay Stanchev]]></dc:creator><pubDate>Sun, 18 Jan 2026 14:35:33 GMT</pubDate><content:encoded><![CDATA[<h1 id="heading-overview">Overview</h1>
<p>The purpose of this post is to introduce <strong>Lambdora</strong> - a side project I’ve been working on that turns <strong>change data capture (CDC)</strong> events into a clear, queryable timeline of how data evolves over time.</p>
<p>Lambdora takes raw CDC streams and transforms them into structured change histories, showing what changed, when it changed, and what a database entity’s state looked like at any point in time. This makes it easier to debug production issues, analyze change patterns, audit data behavior, or simply prove that a specific change actually occurred.</p>
<p>In this post, I’ll explain what Lambdora is and why I built it, walk through its core components and architecture, explore the use cases it’s designed to address, and highlight its key features with a few demos.</p>
<h1 id="heading-background-context">Background Context</h1>
<p>Lambdora started as a small side project over the Christmas holidays. The original goal was simple - I wanted to experiment with a few AI tools and models and see how far they could realistically take me when building something from scratch.</p>
<p>What surprised me most was how quickly the idea came together. With AI assisting throughout design discussions, implementation details, refactoring, and even edge-case reasoning, the project moved from a rough concept to a working prototype in just a few days. I originally shared the idea behind Lambdora in a LinkedIn <a target="_blank" href="https://www.linkedin.com/posts/nikolay-stanchev-2aab02128_i-spent-a-couple-of-days-over-the-holidays-ugcPost-7411780807154974720-TF11?utm_source=share&amp;utm_medium=member_desktop&amp;rcm=ACoAAB980yIB71K8-Yi-l5NzHW_OpmHkKdFH5Ww">post</a>, which you can check out for some early context on how the project started.</p>
<p>As the holidays ended, Lambdora had already grown past a throwaway prototype. The core ideas felt solid, the use cases were real, and the tool was genuinely useful when pointed at actual CDC streams. That’s when I decided to keep building - refining the model, adding features, and treating Lambdora more like a potential production tool rather than a one-off experiment.</p>
<p>The goal of this post is to explore Lambdora in more detail, walk through a few demos, and gather early feedback before making it available for wider use.</p>
<h1 id="heading-what-is-lambdora">What Is Lambdora</h1>
<p>Lambdora is a service that integrates into your existing architecture through a change data capture (CDC) pipeline, for example using Debezium and Kafka. It consumes CDC events emitted by your systems and persistently records all data changes - creates, updates, and deletes - in a structured and queryable form.</p>
<p>Based on these events, Lambdora builds a historical timeline for each data entity. This timeline can be queried across specific time ranges, filtered by operation types, or narrowed down to changes affecting particular fields. The result is a clear and reliable view of how your data evolves over time and what exactly changed at each step.</p>
<h2 id="heading-core-concepts">Core Concepts</h2>
<ul>
<li><p><strong>Source</strong> - the system from which change events originated, e.g. a MySQL database in your application stack</p>
</li>
<li><p><strong>Collection</strong> - a logical grouping for change events within a source, e.g. a table in your MySQL database</p>
</li>
<li><p><strong>Entity</strong> - a single data entity within a collection, e.g. a row in your MySQL DB table</p>
</li>
<li><p><strong>Change Event</strong> - an immutable record indicating that a change occurred on an entity at a specific point in time, e.g. an update event on an entity</p>
</li>
<li><p><strong>Change Event Field</strong> - an individual field change within a change event, capturing how a specific field was modified</p>
</li>
<li><p><strong>Operation</strong> - the action performed in a given change event; Lambdora supports five operation types:</p>
<ul>
<li><p><strong>CREATE</strong> - indicates that an entity was created, e.g. <code>INSERT</code> statements in your MySQL DB table; this is either the first event for an entity or must occur after a delete event</p>
</li>
<li><p><strong>UPDATE</strong> - indicates that one or more fields of an existing entity were modified, e.g. <code>UPDATE</code> statements in your MySQL DB table</p>
</li>
<li><p><strong>DELETE</strong> - indicates that an entity was deleted, e.g. <code>DELETE</code> statements in your MySQL DB table</p>
</li>
<li><p><strong>BACKFILL</strong> - indicates that an existing entity from the source system was created in Lambdora during the initial backfill when Lambdora was first bootstrapped for the given source; it is similar to a create event, but occurs only as part of a backfill process</p>
</li>
<li><p><strong>SNAPSHOT</strong> - a Lambdora-specific operation representing a compaction result. Multiple historical events are collapsed into a single snapshot event that captures the accumulated changes. Snapshots can be created automatically based on retention rules (such as maximum event age or count per entity) or triggered manually via the Lambdora UI</p>
</li>
</ul>
</li>
<li><p><strong>Change History</strong> - a chronological sequence of change events that represents the lifecycle of an entity over time</p>
</li>
<li><p><strong>Entity State</strong> - the complete reconstructed state of an entity at a specific point in time, derived by applying its change history up to that moment</p>
</li>
<li><p><strong>Entity Statistics</strong> - several metrics describing an entity’s lifecycle, including timestamps of the first and most recent change events and a breakdown of event counts by operation type. Statistics are available in two forms:</p>
<ul>
<li><p><strong>Lifetime Statistics</strong> - metrics that reflect the full lifecycle of an entity as observed by Lambdora, regardless of compaction; these counters include all events ever received, even if some of them have since been compacted</p>
</li>
<li><p><strong>Queryable Statistcs</strong> - metrics calculated only from the change events that currently exist in Lambdora; events removed through compaction are excluded from these counts</p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-architecture-amp-components">Architecture &amp; Components</h2>
<p>Lambdora is designed to integrate cleanly with existing CDC-based architectures, layering observability and historical insight on top of your data changes without interfering with production systems.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768733813955/d316b802-8fb7-40a1-b600-97f2551b4ed8.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-source-databases"><strong>Source Databases</strong></h3>
<p>Lambdora works with one or more source systems, such as <strong>MySQL</strong> or <strong>PostgreSQL</strong>, where data changes originate. These systems remain entirely unaware of Lambdora.</p>
<h3 id="heading-debezium-kafka-connectors"><strong>Debezium Kafka Connectors</strong></h3>
<p>Debezium connectors monitor the source databases and capture row-level changes (inserts, updates, deletes). These changes are emitted as CDC events and published to Kafka topics in a reliable, ordered manner.</p>
<h3 id="heading-kafka-cdc-topics"><strong>Kafka CDC Topics</strong></h3>
<p>Kafka acts as the transport layer for CDC events. Topics provide buffering, ordering guarantees, and replayability, allowing Lambdora to process events asynchronously and at its own pace.</p>
<h3 id="heading-lambdora-cdc-processor"><strong>Lambdora CDC Processor</strong></h3>
<p>The CDC processor consumes events from Kafka, normalizes them into Lambdora’s internal change event model, and applies validation and enrichment logic (such as operation type classification and field-level diffs).</p>
<h3 id="heading-lambdora-core"><strong>Lambdora Core</strong></h3>
<p>The core service persists change events, reconstructs entity state, maintains statistics, and applies retention rules. It exposes APIs for querying timelines, inspecting diffs, and time-traveling to historical states.</p>
<h3 id="heading-lambdora-ui"><strong>Lambdora UI</strong></h3>
<p>The UI sits on top of the core API and provides an interactive way for users to explore entity history, inspect changes over time, view statistics, and trigger manual compaction when needed.</p>
<h2 id="heading-key-features">Key Features</h2>
<p>Lambdora focuses on making CDC data easy to understand, explore, and reason about. Rather than exposing raw event streams, it provides higher-level primitives for inspecting how data evolves over time.</p>
<h3 id="heading-field-level-change-history">Field-Level Change History</h3>
<p>Lambdora captures changes at the field level, showing exactly what changed, from what value, and to what value for every event. This makes it easy to understand the intent and impact of each update, not just that a row changed.</p>
<h3 id="heading-time-travel-queries">Time-Travel Queries</h3>
<p>You can query an entity’s state at any point in time. Lambdora reconstructs the full entity state by replaying relevant change events (and snapshots when available), enabling historical inspection and point-in-time debugging.</p>
<h3 id="heading-queryable-change-timelines">Queryable Change Timelines</h3>
<p>Change events can be queried by:</p>
<ul>
<li><p>Time range</p>
</li>
<li><p>Operation type (create, update, delete, backfill, snapshot)</p>
</li>
<li><p>Affected fields</p>
</li>
</ul>
<p>This allows you to narrow down large histories to only the changes that matter for a specific investigation.</p>
<h3 id="heading-automated-and-manual-compaction">Automated and Manual Compaction</h3>
<p>To keep storage and query performance under control, Lambdora supports event compaction:</p>
<ul>
<li><p>Automated compaction based on event age or events count</p>
</li>
<li><p>Manual compaction triggered via the UI</p>
</li>
</ul>
<p>Compaction replaces multiple historical events with a snapshot that preserves the effective state while reducing history size.</p>
<h3 id="heading-snapshot-and-backfill-awareness">Snapshot and Backfill Awareness</h3>
<p>Lambdora distinguishes between:</p>
<ul>
<li><p>Backfill events, created during initial bootstrap</p>
</li>
<li><p>Snapshot events, created during compaction</p>
</li>
</ul>
<p>This makes it clear how and why an entity’s history was shaped over time, rather than treating all events as identical.</p>
<h3 id="heading-entity-level-statistics">Entity-Level Statistics</h3>
<p>For each entity, Lambdora exposes:</p>
<ul>
<li><p>First and last change event timestamps</p>
</li>
<li><p>Total number of events and a breakdown by operation type</p>
</li>
</ul>
<p>Statistics are available both as:</p>
<ul>
<li><p>Queryable statistics (based on retained events)</p>
</li>
<li><p>Lifetime statistics (reflecting the full history, even after compaction)</p>
</li>
</ul>
<h3 id="heading-interactive-ui-for-exploration">Interactive UI for Exploration</h3>
<p>The Lambdora UI provides a visual way to:</p>
<ul>
<li><p>Inspect current and historical entity state</p>
</li>
<li><p>Browse change timelines</p>
</li>
<li><p>View statistics</p>
</li>
<li><p>Copy entity state as JSON</p>
</li>
<li><p>Trigger compaction actions</p>
</li>
</ul>
<p>This makes CDC data accessible not only to backend engineers but also to anyone analyzing data behavior for a specific database.</p>
<h1 id="heading-demos">Demos</h1>
<p>To make Lambdora’s concepts more concrete, this section walks through a series of short demos based on two instances of a simple orders service backed by relational databases - one using MySQL and the other USING PostgreSQL. Change events are captured via Debezium, processed by Lambdora, and explored through the UI.</p>
<p>Each demo highlights a specific capability and builds progressively on the previous one.</p>
<h2 id="heading-exploring-the-current-entity-state">Exploring the Current Entity State</h2>
<p>The first demo shows how Lambdora allows you to query the current state of an entity.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/IADh3fwOpVE">https://youtu.be/IADh3fwOpVE</a></div>
<p> </p>
<h2 id="heading-browsing-the-change-timeline-of-an-entity">Browsing the Change Timeline of an Entity</h2>
<p>Next, the demo switches to the change history view. This view presents a chronological timeline of all change events for an entity, allowing you to filter by time range, operation type, or specific fields. It also supports time-traveling through the entity’s history, making it possible to inspect how the entity evolved and reconstruct its state at any point in time.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/lPWkNYK-zXY">https://youtu.be/lPWkNYK-zXY</a></div>
<p> </p>
<h2 id="heading-compaction-in-action">Compaction in Action</h2>
<p>The next demo focuses on compaction of change events, showing how Lambdora reduces long histories into efficient snapshots without losing the ability to query state accurately.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/zlIfI_pO-Kg">https://youtu.be/zlIfI_pO-Kg</a></div>
<p> </p>
<h2 id="heading-exploring-entity-statistics">Exploring Entity Statistics</h2>
<p>The next demo explores entity statistics and highlights the difference between queryable and lifetime metrics. Because compaction was performed in the previous demo, the number of queryable events is lower than the lifetime counts, which include events that have since been compacted.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/yersOLd9UO0">https://youtu.be/yersOLd9UO0</a></div>
<p> </p>
<h1 id="heading-next-steps">Next Steps</h1>
<p>Lambdora is still in its early stages, but I’m actively working toward making it easier for others to try it out in real environments. A key focus right now is preparing production-ready Docker images along with deployment templates that simplify running Lambdora locally or in managed infrastructure.</p>
<p>In parallel, I plan to share progress and updates regularly as the project evolves. The goal is to gather early feedback from engineers and practitioners who work with CDC and data-intensive systems, and to use that feedback for shaping Lambdora’s direction as it matures.</p>
<p>If this post has sparked your interest in Lambdora or you’d like to explore it further, feel free to reach out to me on <a target="_blank" href="https://www.linkedin.com/in/nikolay-stanchev-2aab02128/">LinkedIn</a> - feedback and discussion are very welcome.</p>
]]></content:encoded></item><item><title><![CDATA[Adding Authentication to a Java REST API]]></title><description><![CDATA[Introduction
This post is another follow-up in the series of articles covering the technical process of building a fully fletched Java REST API for task management:

https://blog.teonisoftware.com/step-by-step-tutorial-for-building-a-rest-api-in-java...]]></description><link>https://blog.teonibyte.com/adding-authentication-to-a-java-rest-api</link><guid isPermaLink="true">https://blog.teonibyte.com/adding-authentication-to-a-java-rest-api</guid><category><![CDATA[Java]]></category><category><![CDATA[REST API]]></category><category><![CDATA[authentication]]></category><category><![CDATA[Docker]]></category><category><![CDATA[Kratos]]></category><dc:creator><![CDATA[Nikolay Stanchev]]></dc:creator><pubDate>Sun, 27 Aug 2023 14:47:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/lmFJOx7hPc4/upload/d60a6437705b55d9ca255b7a16c52d75.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>This post is another follow-up in the series of articles covering the technical process of building a fully fletched Java REST API for task management:</p>
<ul>
<li><p><a target="_blank" href="https://blog.teonisoftware.com/step-by-step-tutorial-for-building-a-rest-api-in-java">https://blog.teonisoftware.com/step-by-step-tutorial-for-building-a-rest-api-in-java</a></p>
</li>
<li><p><a target="_blank" href="https://blog.teonisoftware.com/integrating-a-java-rest-api-with-a-database">https://blog.teonisoftware.com/integrating-a-java-rest-api-with-a-database</a></p>
</li>
<li><p><a target="_blank" href="https://blog.teonisoftware.com/adding-logging-and-metrics-to-a-java-rest-api">https://blog.teonisoftware.com/adding-logging-and-metrics-to-a-java-rest-api</a></p>
</li>
</ul>
<p>So far, we have built the API itself, we added a storage layer using <a target="_blank" href="https://www.mongodb.com">MongoDB</a> and in the last post, we added logging functionalities as well as metrics collected through the use of <a target="_blank" href="https://prometheus.io/">Prometheus</a>. Now, we are going to add another crucial part for every API implementation - authentication. To achieve this we are going to use another open-source product called <a target="_blank" href="https://www.ory.sh/kratos/">Kratos</a> that will provide all the standard user management features for us - registration, login, logout, email verification, etc. The authentication for the task management API will be token-based - that is the API client will register and login with Kratos and thus will receive a session token generated by Kratos. This session token will then be passed in the HTTP Authorization header in each call to the API. The task management service will extract this token from the request and call Kratos API to convert it to the corresponding user session. The extracted user will be used when creating new tasks so that we can keep a record of who created the task itself.</p>
<h2 id="heading-current-state">Current State</h2>
<p>In the current setup, the local deployment consists of three containers:</p>
<ul>
<li><p>one for running the task management service logic</p>
</li>
<li><p>one for running the storage layer - a MongoDB instance that stores the list of all tasks</p>
</li>
<li><p>one for running the metrics logic - a Prometheus instance as well as a Prometheus job that periodically pulls metrics from the task management service</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1693127786802/eeba8d98-2828-419f-b371-30bcc9f7e3a7.png" alt class="image--center mx-auto" /></p>
<p>Docker is used as the container runtime environment and docker-compose is used to orchestrate the multi-container local deployment.</p>
<p>From a business logic perspective, tasks are currently anonymized - there is no notion of a task creator.</p>
<h2 id="heading-target-state">Target State</h2>
<p>What we want to achieve at the end of this tutorial is the diagram below. The green colour is used to indicate a new component or piece of software.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1693129887529/e471ac81-49d0-44a3-bdf3-32c50b75bfb0.png" alt class="image--center mx-auto" /></p>
<p>From the diagram, we can see three major changes:</p>
<ul>
<li><p>new containers for deploying Kratos</p>
<ul>
<li><p>one for running the Kratos service itself</p>
</li>
<li><p>one for running an auxiliary pre-built self-service Kratos UI - we will use this for registering a new user</p>
</li>
<li><p>one for running a mock email server to be able to send and receive emails in a local setup without using a real email provider - we will use this for verifying the email of the registered user</p>
</li>
</ul>
</li>
<li><p>new request filter used to implement authentication by using Kratos API</p>
</li>
<li><p>a new field in the Task entity used to store a reference to the user that created the task itself</p>
</li>
</ul>
<h2 id="heading-implementation">Implementation</h2>
<p>To get to the target state, we are going to break the implementation into small steps so that the process of adding authentication can be easily understood as you follow along this tutorial.</p>
<h3 id="heading-adding-authentication-skeleton-logic">Adding Authentication Skeleton Logic</h3>
<p>The first step is to add a basic authentication skeleton logic:</p>
<ul>
<li><p>extracting an authentication token from the HTTP request</p>
</li>
<li><p>converting it to the newly introduced user entity</p>
</li>
<li><p>using the user ID as a reference for the new <em>createdBy</em> field for each task</p>
</li>
</ul>
<p>We start by adding the new <em>createdBy</em> reference to the Task entity class. This will represent the ID of the user who created the task.</p>
<pre><code class="lang-java"> <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Task</span> </span>{

    ...

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String createdBy;

    ...

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TaskBuilder</span> </span>{

        ...

        <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-title">TaskBuilder</span><span class="hljs-params">(String title, String description, String createdBy)</span> </span>{
            validateArgNotNullOrBlank(title, <span class="hljs-string">"title"</span>);
            validateArgNotNullOrBlank(description, <span class="hljs-string">"description"</span>);
            validateArgNotNullOrBlank(createdBy, <span class="hljs-string">"createdBy"</span>);

            <span class="hljs-keyword">this</span>.title = title;
            <span class="hljs-keyword">this</span>.description = description;
            <span class="hljs-keyword">this</span>.createdBy = createdBy;
            <span class="hljs-keyword">this</span>.identifier = UUID.randomUUID().toString();
            <span class="hljs-keyword">this</span>.createdAt = Instant.now();
            <span class="hljs-keyword">this</span>.completed = <span class="hljs-keyword">false</span>;
        }

        ...

    }
}
</code></pre>
<p>Then, we introduce the new User entity class. For now, we only need to retain the user ID. We won't need any more data about the user itself.</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String userID;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">User</span><span class="hljs-params">(String userID)</span> </span>{
        validateArgNotNull(<span class="hljs-string">"userID"</span>, userID);

        <span class="hljs-keyword">this</span>.userID = userID;
    }

    ...

}
</code></pre>
<p>Now that we have the new entity class in place, we can change the service class logic to accept a <strong>User</strong> instance as the creator for the given task. This user instance will represent the authenticated user calling the API.</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TaskManagementService</span> </span>{

    ...    

    <span class="hljs-function"><span class="hljs-keyword">public</span> Task <span class="hljs-title">create</span><span class="hljs-params">(String title, String description, User creator)</span> </span>{
        validateArgNotNull(creator, <span class="hljs-string">"creator"</span>);

        Task task = Task.builder(title, description, creator.getUserID()).build();

        repository.save(task);

        LOGGER.info(<span class="hljs-string">"Successfully created new task {}"</span>, task);

        <span class="hljs-keyword">return</span> task;
    }

    ...

}
</code></pre>
<p>The three code blocks above pretty much summarize the changes needed to be done for the business logic layer. As for the storage layer, a few small changes are needed to ensure that the new <em>createdBy</em> field is stored in the database:</p>
<pre><code class="lang-java">    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MongoDBTask</span> </span>{

        ...

        <span class="hljs-meta">@BsonProperty("created_by")</span>
        <span class="hljs-keyword">private</span> String createdBy;

        ...

        <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getCreatedBy</span><span class="hljs-params">()</span> </span>{
            <span class="hljs-keyword">return</span> createdBy;
        }

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setCreatedBy</span><span class="hljs-params">(String createdBy)</span> </span>{
            <span class="hljs-keyword">this</span>.createdBy = createdBy;
        }

        ...

    }
</code></pre>
<p>Now, let's move to the API layer. We start by introducing a new request filter, namely the AuthenticationFilter responsible for extracting the authentication token from the HTTP Authorization Header, converting it to a user instance and propagating the logged in user through the request context as a property. Because we don't have Kratos setup yet, we are going to implement a temporary logic that uses the provided authentication token as the user ID. The rest of the logic is pretty straightforward - fetching the HTTP header, validating the header value and performing a few string manipulations to extract the token from it.</p>
<pre><code class="lang-java"><span class="hljs-meta">@Priority(3)</span>
<span class="hljs-meta">@Provider</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AuthenticationFilter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ContainerRequestFilter</span> </span>{

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">filter</span><span class="hljs-params">(ContainerRequestContext requestContext)</span> </span>{
        String authHeader = requestContext.getHeaderString(<span class="hljs-string">"Authorization"</span>);
        <span class="hljs-keyword">if</span> (authHeader == <span class="hljs-keyword">null</span>) {
            <span class="hljs-keyword">return</span>;
        }

        authHeader = authHeader.trim();
        <span class="hljs-keyword">if</span> (!authHeader.startsWith(<span class="hljs-string">"Bearer"</span>)) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> AuthenticationException(<span class="hljs-string">"invalid authentication scheme - please use a Bearer token"</span>);
        }

        String authToken = authHeader.replaceFirst(<span class="hljs-string">"Bearer"</span>, <span class="hljs-string">""</span>).trim();
        User user = getUserByAuthToken(authToken);
        requestContext.setProperty(<span class="hljs-string">"user"</span>, user);
    }

    <span class="hljs-function"><span class="hljs-keyword">private</span> User <span class="hljs-title">getUserByAuthToken</span><span class="hljs-params">(String authToken)</span> </span>{
        <span class="hljs-comment">// FIXME for now use the auth token as user ID until integration with user service API is implemented</span>
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> User(authToken);
    }
}
</code></pre>
<p>Something to notice here is that we use priority 3 which is more than what we've used in the <a target="_blank" href="https://blog.teonisoftware.com/adding-logging-and-metrics-to-a-java-rest-api">previous tutorial</a> for the logging and the metrics filters. This ensures that the logging and metrics request filters are still going to run first and only then the authentication request filter will be executed. This is important to properly capture the request processing start time - see this <a target="_blank" href="https://blog.teonisoftware.com/adding-logging-and-metrics-to-a-java-rest-api#heading-writing-logs">part</a> of the previous tutorial for more details.</p>
<p>As with any other filters, we need to register it in the application config so that it's used at runtime:</p>
<pre><code class="lang-java"><span class="hljs-meta">@ApplicationPath("/api")</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ApplicationConfig</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ResourceConfig</span> </span>{
    <span class="hljs-meta">@Inject</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">ApplicationConfig</span><span class="hljs-params">(ServiceLocator serviceLocator)</span> </span>{
        ...
        register(LoggingFilter.class);
        register(MetricsFilter.class);
        register(AuthenticationFilter.class);

        ...
    }
}
</code></pre>
<p>Now that a user instance is included in the request context, we can use it in the task management resource class which implements the API endpoints:</p>
<pre><code class="lang-java"><span class="hljs-meta">@Path("/tasks")</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TaskManagementResource</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> TaskManagementService service;

    ...

    <span class="hljs-meta">@POST</span>
    <span class="hljs-meta">@Consumes(MediaType.APPLICATION_JSON)</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Response <span class="hljs-title">createTask</span><span class="hljs-params">(TaskCreateRequest taskCreateRequest, <span class="hljs-meta">@Context</span> ContainerRequestContext requestContext)</span> </span>{
        validateArgNotNull(taskCreateRequest, <span class="hljs-string">"task-create-request-body"</span>);

        Task task = service.create(taskCreateRequest.getTitle(), taskCreateRequest.getDescription());
        User user = (User) requestContext.getProperty(<span class="hljs-string">"user"</span>);
        Task task = service.create(taskCreateRequest.getTitle(), taskCreateRequest.getDescription(), user);

        ...
    }

    ...

}
</code></pre>
<p>Two key changes here:</p>
<ul>
<li><p>we used the Context annotation to instruct Jersey to inject the ContainerRequestContext instance when invoking the endpoint logic</p>
</li>
<li><p>we used the request context to read the <em>user</em> property set by the authentication filter and propagate the <strong>User</strong> instance to the business logic service class</p>
</li>
</ul>
<p>With this last step, the authentication skeleton logic is finalized. A few things that I've skipped explaining for clarity are a couple of auxiliary changes:</p>
<ul>
<li><p>adding a new AuthenticationException to handle exceptional behaviour during authentication - e.g. using the <strong>Basic</strong> scheme in the HTTP Authorization header rather than <strong>Bearer</strong> which is the standard for token-based authentication</p>
</li>
<li><p>adding a new exception mapper that converts uncaught authentication exceptions to responses with <strong>401 Unauthorized</strong> status codes</p>
</li>
</ul>
<p>The full commit for what we just covered can be found <a target="_blank" href="https://github.com/nikist97/TaskManagementService/commit/3bf429635634dab81233bf0f0a04547fcc0f7c3d">here</a>.</p>
<h3 id="heading-kratos-container-setup">Kratos Container Setup</h3>
<p>The next step for this tutorial is to add the Kratos configuration for local deployment. I am not planning to go into details about Kratos and how it works to keep this blog post simple and focused on the purpose of building a Java REST API. However, it is worth mentioning that the setup and configuration that we are using are all based on the quickstart tutorial provided by Ory on the official Kratos documentation page - <a target="_blank" href="https://www.ory.sh/docs/kratos/quickstart">https://www.ory.sh/docs/kratos/quickstart</a>. Feel free to explore the Kratos documentation for more details.</p>
<p>All we need to know for this tutorial is that Kratos requires its own configuration file for deployment as well as at least one <a target="_blank" href="https://json-schema.org/">JSON schema</a> configuration file that defines the default user identity.</p>
<p>We start by defining the user schema. For now, we don't need anything else apart from an email property. If you are not familiar with the JSON schema language, please refer to the official <a target="_blank" href="https://json-schema.org/learn/">documentation</a>.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"$id"</span>: <span class="hljs-string">"http://localhost/schemas/user.schema.json"</span>,
  <span class="hljs-attr">"$schema"</span>: <span class="hljs-string">"http://json-schema.org/draft-07/schema#"</span>,
  <span class="hljs-attr">"title"</span>: <span class="hljs-string">"User"</span>,
  <span class="hljs-attr">"type"</span>: <span class="hljs-string">"object"</span>,
  <span class="hljs-attr">"properties"</span>: {
    <span class="hljs-attr">"traits"</span>: {
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"object"</span>,
      <span class="hljs-attr">"properties"</span>: {
        <span class="hljs-attr">"email"</span>: {
          <span class="hljs-attr">"type"</span>: <span class="hljs-string">"string"</span>,
          <span class="hljs-attr">"format"</span>: <span class="hljs-string">"email"</span>,
          <span class="hljs-attr">"title"</span>: <span class="hljs-string">"Email"</span>,
          <span class="hljs-attr">"minLength"</span>: <span class="hljs-number">3</span>,
          <span class="hljs-attr">"ory.sh/kratos"</span>: {
            <span class="hljs-attr">"credentials"</span>: {
              <span class="hljs-attr">"password"</span>: {
                <span class="hljs-attr">"identifier"</span>: <span class="hljs-literal">true</span>
              }
            },
            <span class="hljs-attr">"verification"</span>: {
              <span class="hljs-attr">"via"</span>: <span class="hljs-string">"email"</span>
            },
            <span class="hljs-attr">"recovery"</span>: {
              <span class="hljs-attr">"via"</span>: <span class="hljs-string">"email"</span>
            }
          }
        }
      },
      <span class="hljs-attr">"required"</span>: [
        <span class="hljs-string">"email"</span>
      ],
      <span class="hljs-attr">"additionalProperties"</span>: <span class="hljs-literal">false</span>
    }
  }
}
</code></pre>
<p>Next, we add the Kratos configuration file. Again, this is very specific to Kratos itself, so please refer to the official <a target="_blank" href="https://www.ory.sh/docs/kratos/ory-kratos-intro">documentation</a> for more details. An important bit to notice though is that we use the user schema defined above as the default identity schema (check the line with default_schema_id).</p>
<pre><code class="lang-yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">v0.13.0</span>

<span class="hljs-attr">serve:</span>
  <span class="hljs-attr">public:</span>
    <span class="hljs-attr">base_url:</span> <span class="hljs-string">http://127.0.0.1:4433/</span>
    <span class="hljs-attr">cors:</span>
      <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">admin:</span>
    <span class="hljs-attr">base_url:</span> <span class="hljs-string">http://kratos:4434/</span>

<span class="hljs-attr">selfservice:</span>
  <span class="hljs-attr">default_browser_return_url:</span> <span class="hljs-string">http://127.0.0.1:4455/</span>
  <span class="hljs-attr">allowed_return_urls:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">http://127.0.0.1:4455</span>

  <span class="hljs-attr">methods:</span>
    <span class="hljs-attr">password:</span>
      <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span>
    <span class="hljs-attr">code:</span>
      <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span>

  <span class="hljs-attr">flows:</span>
    <span class="hljs-attr">error:</span>
      <span class="hljs-attr">ui_url:</span> <span class="hljs-string">http://127.0.0.1:4455/error</span>

    <span class="hljs-attr">settings:</span>
      <span class="hljs-attr">ui_url:</span> <span class="hljs-string">http://127.0.0.1:4455/settings</span>
      <span class="hljs-attr">privileged_session_max_age:</span> <span class="hljs-string">15m</span>
      <span class="hljs-attr">required_aal:</span> <span class="hljs-string">highest_available</span>

    <span class="hljs-attr">recovery:</span>
      <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">ui_url:</span> <span class="hljs-string">http://127.0.0.1:4455/recovery</span>
      <span class="hljs-attr">use:</span> <span class="hljs-string">code</span>

    <span class="hljs-attr">verification:</span>
      <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">ui_url:</span> <span class="hljs-string">http://127.0.0.1:4455/verification</span>
      <span class="hljs-attr">use:</span> <span class="hljs-string">code</span>
      <span class="hljs-attr">after:</span>
        <span class="hljs-attr">default_browser_return_url:</span> <span class="hljs-string">http://127.0.0.1:4455/</span>

    <span class="hljs-attr">logout:</span>
      <span class="hljs-attr">after:</span>
        <span class="hljs-attr">default_browser_return_url:</span> <span class="hljs-string">http://127.0.0.1:4455/login</span>

    <span class="hljs-attr">login:</span>
      <span class="hljs-attr">lifespan:</span> <span class="hljs-string">10m</span>
      <span class="hljs-attr">ui_url:</span> <span class="hljs-string">http://127.0.0.1:4455/login</span>

    <span class="hljs-attr">registration:</span>
      <span class="hljs-attr">lifespan:</span> <span class="hljs-string">10m</span>
      <span class="hljs-attr">ui_url:</span> <span class="hljs-string">http://127.0.0.1:4455/registration</span>
      <span class="hljs-attr">after:</span>
        <span class="hljs-attr">password:</span>
          <span class="hljs-attr">hooks:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">hook:</span> <span class="hljs-string">session</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">hook:</span> <span class="hljs-string">show_verification_ui</span>

<span class="hljs-attr">log:</span>
  <span class="hljs-attr">level:</span> <span class="hljs-string">debug</span>
  <span class="hljs-attr">format:</span> <span class="hljs-string">text</span>

<span class="hljs-attr">secrets:</span>
  <span class="hljs-attr">cookie:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">PLEASE-CHANGE-ME-I-AM-VERY-INSECURE</span>
  <span class="hljs-attr">cipher:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-number">32</span><span class="hljs-string">-LONG-SECRET-NOT-SECURE-AT-ALL</span>

<span class="hljs-attr">identity:</span>
  <span class="hljs-attr">default_schema_id:</span> <span class="hljs-string">user</span>
  <span class="hljs-attr">schemas:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">user</span>
      <span class="hljs-attr">url:</span> <span class="hljs-string">file:///etc/config/kratos/user.schema.json</span>

<span class="hljs-attr">courier:</span>
  <span class="hljs-attr">smtp:</span>
    <span class="hljs-attr">connection_uri:</span> <span class="hljs-string">smtps://test:test@mailslurper:1025/?skip_ssl_verify=true</span>
</code></pre>
<p>Finally, we add the container setup to the existing <a target="_blank" href="https://docs.docker.com/compose/">docker-compose</a> file, so that Kratos is included in our local deployment.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">"3.9"</span>
<span class="hljs-attr">services:</span>
  <span class="hljs-string">...</span>
  <span class="hljs-attr">kratos:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">oryd/kratos:v1.0.0</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">taskmanagementservice-kratos</span>
    <span class="hljs-attr">depends_on:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">kratos-migrate</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">mailslurper</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"4433:4433"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"4434:4434"</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">DSN=sqlite:///var/lib/sqlite/db.sqlite?_fk=true</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">LOG_LEVEL=trace</span>
    <span class="hljs-attr">command:</span> <span class="hljs-string">serve</span> <span class="hljs-string">-c</span> <span class="hljs-string">/etc/config/kratos/kratos.yml</span> <span class="hljs-string">--dev</span> <span class="hljs-string">--watch-courier</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">.data/kratos:/var/lib/sqlite/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./kratos.yml:/etc/config/kratos/kratos.yml</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./user.schema.json:/etc/config/kratos/user.schema.json</span>
    <span class="hljs-attr">networks:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">intranet</span>
  <span class="hljs-attr">kratos-migrate:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">oryd/kratos:v1.0.0</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">taskmanagementservice-kratos-migrate</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">DSN=sqlite:///var/lib/sqlite/db.sqlite?_fk=true&amp;mode=rwc</span>
    <span class="hljs-attr">command:</span> <span class="hljs-string">-c</span> <span class="hljs-string">/etc/config/kratos/kratos.yml</span> <span class="hljs-string">migrate</span> <span class="hljs-string">sql</span> <span class="hljs-string">-e</span> <span class="hljs-string">--yes</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">.data/kratos:/var/lib/sqlite/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./kratos.yml:/etc/config/kratos/kratos.yml</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./user.schema.json:/etc/config/kratos/user.schema.json</span>
    <span class="hljs-attr">networks:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">intranet</span>
  <span class="hljs-attr">kratos-selfservice-ui-node:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">oryd/kratos-selfservice-ui-node:v1.0.0</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">taskmanagementservice-kratos-selfservice-ui-node</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"4455:4455"</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KRATOS_PUBLIC_URL=http://taskmanagementservice-kratos:4433/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KRATOS_BROWSER_URL=http://127.0.0.1:4433/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">PORT=4455</span>
    <span class="hljs-attr">networks:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">intranet</span>
  <span class="hljs-attr">mailslurper:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">oryd/mailslurper:latest-smtps</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">taskmanagementservice-mailslurper</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"4436:4436"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"4437:4437"</span>
    <span class="hljs-attr">networks:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">intranet</span>
<span class="hljs-attr">networks:</span>
  <span class="hljs-attr">intranet:</span>
</code></pre>
<p>Important bits to notice in this change are:</p>
<ul>
<li><p>we are adding 4 new containers to our local setup</p>
<ul>
<li><p><em>kratos</em> is the container running the Kratos API itself</p>
</li>
<li><p><em>kratos-migrate</em> is a single-command container used for applying SQL migrations the first time the deployment is started, after which the container is no longer used</p>
</li>
<li><p><em>kratos-self-service-ui-node</em> is a pre-built UI for self-service user management provided by the Ory Kratos team themselves - we use this to make user registration easier for the purpose of this tutorial (ideally, we would build our own UI - Kratos is quite flexible when it comes to UI choice)</p>
</li>
<li><p>mailslurper is the container that will handle email verification during registration; Kratos will use this as a stub for sending and receiving emails so that we don't have to configure a real email provider</p>
</li>
</ul>
</li>
<li><p>we are binding the Kratos configuration files defined above to the corresponding locations on the containers where Kratos would expect to find them</p>
</li>
<li><p>we are instructing Kratos to use SQLite as storage layer</p>
</li>
</ul>
<p>This concludes the changes needed to deploy Kratos locally. The full commit can be found <a target="_blank" href="https://github.com/nikist97/TaskManagementService/commit/245ba7dc7b495ab309880eea6ab1c141814bbccc">here</a>.</p>
<p>To test the deployment we can use the standard docker-compose build commands we used in the previous blog posts.</p>
<p>To start the deployment and rebuild the task management service container:</p>
<pre><code class="lang-bash">docker-compose up --build -d
</code></pre>
<p>To stop the deployment:</p>
<pre><code class="lang-bash"> docker-compose down
</code></pre>
<h3 id="heading-integration-with-kratos-api">Integration with Kratos API</h3>
<p>Now, that we have the skeleton logic in place along with the local deployment setup for Kratos we can integrate the two services. First, we define the interface for the user-related functionalities needed by the task management service:</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">UserManagementService</span> </span>{
    <span class="hljs-function">Optional&lt;User&gt; <span class="hljs-title">getUserByAuthToken</span><span class="hljs-params">(String authToken)</span> <span class="hljs-keyword">throws</span> IOException</span>;
}
</code></pre>
<p>For now, we only need a single method that accepts an authentication token as input and returns an optional user. The return is optional because the auth token might be invalid or might not correspond to any active user session.</p>
<p>Then, we add a Kratos-based implementation of this interface along with the Kratos client maven dependency:</p>
<pre><code class="lang-xml">    ...
    <span class="hljs-tag">&lt;<span class="hljs-name">dependencies</span>&gt;</span>
        ...
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>sh.ory.kratos<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>kratos-client<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>0.13.1<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependencies</span>&gt;</span>
    ...
</code></pre>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">KratosWrapper</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">UserManagementService</span> </span>{

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> FrontendApi kratosFrontendApi;

    <span class="hljs-meta">@Inject</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">KratosWrapper</span><span class="hljs-params">(FrontendApi kratosFrontendApi)</span> </span>{
        <span class="hljs-keyword">this</span>.kratosFrontendApi = kratosFrontendApi;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> Optional&lt;User&gt; <span class="hljs-title">getUserByAuthToken</span><span class="hljs-params">(String authToken)</span> <span class="hljs-keyword">throws</span> IOException </span>{
        Session session;
        <span class="hljs-keyword">try</span> {
            session = kratosFrontendApi.toSession(authToken, <span class="hljs-keyword">null</span>);
        } <span class="hljs-keyword">catch</span> (ApiException e) {
            <span class="hljs-keyword">if</span> (e.getCode() == Response.Status.UNAUTHORIZED.getStatusCode()) {
                <span class="hljs-keyword">return</span> Optional.empty();
            }

            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IOException(e);
        }

        <span class="hljs-keyword">if</span> (session == <span class="hljs-keyword">null</span>) {
            <span class="hljs-keyword">return</span> Optional.empty();
        }

        <span class="hljs-keyword">if</span> (session.getActive() == <span class="hljs-keyword">null</span> || !session.getActive()){
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> AuthenticationException(<span class="hljs-string">"Inactive session returned for the given authentication token."</span>);
        }

        <span class="hljs-keyword">return</span> Optional.of(<span class="hljs-keyword">new</span> User(session.getIdentity().getId()));
    }
}
</code></pre>
<p>You might notice that the Kratos FrontentApi instance is injected into the constructor which means we need to add a new Guice module that initializes it:</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">KratosModule</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractModule</span> </span>{

    <span class="hljs-meta">@Provides</span>
    <span class="hljs-function"><span class="hljs-keyword">private</span> FrontendApi <span class="hljs-title">provideKratosFrontendApi</span><span class="hljs-params">()</span> </span>{
        ApiClient apiClient = <span class="hljs-keyword">new</span> ApiClient();
        apiClient.setBasePath(System.getenv(<span class="hljs-string">"Kratos_Base_Path"</span>));

        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> FrontendApi(apiClient);
    }
}
</code></pre>
<p>The base path for the API client we configure through an environment variable - we used the same mechanism for configuring the integration between the task management service and the MongoDB instance in a <a target="_blank" href="https://blog.teonisoftware.com/integrating-a-java-rest-api-with-a-database">previous tutorial</a>.</p>
<p>The new environment variable we configure in the docker-compose file used for local deployment:</p>
<pre><code class="lang-yaml">  <span class="hljs-string">...</span>
  <span class="hljs-attr">webapp:</span>
    <span class="hljs-attr">build:</span> <span class="hljs-string">.</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">taskmanagementservice-api</span>
    <span class="hljs-attr">depends_on:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"mongo"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"kratos"</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">MongoDB_URI=mongodb://taskmanagementservice-mongodb:27017</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">Kratos_Base_Path=http://taskmanagementservice-kratos:4433</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"8080:8080"</span>
    <span class="hljs-attr">networks:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">intranet</span> 
  <span class="hljs-string">...</span>
</code></pre>
<p>To connect all the pieces, we need to also bind the <strong>UserManagementService</strong> interface to the <strong>KratosWrapper</strong> implementation in the main Guice application module so that any classes that require an instance of the <strong>UserManagementService</strong> interface will use an instance of the <strong>KratosWrapper</strong> class.</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ApplicationModule</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractModule</span> </span>{

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">configure</span><span class="hljs-params">()</span> </span>{
        bind(TaskManagementRepository.class).to(MongoDBTaskManagementRepository.class).in(Singleton.class);
        bind(UserManagementService.class).to(KratosWrapper.class).in(Singleton.class);

        install(<span class="hljs-keyword">new</span> MongoDBModule());
        install(<span class="hljs-keyword">new</span> KratosModule());
    }

}
</code></pre>
<p>The final step is to change the dummy <strong>AuthenticationFilter</strong> logic we added in the first commit so that it uses the <strong>UserManagementService</strong> to retrieve a <strong>User</strong> instance given an authentication token.</p>
<pre><code class="lang-java"><span class="hljs-meta">@Priority(3)</span>
<span class="hljs-meta">@Provider</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AuthenticationFilter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ContainerRequestFilter</span> </span>{

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> UserManagementService userManagementService;

    <span class="hljs-meta">@Inject</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">AuthenticationFilter</span><span class="hljs-params">(UserManagementService userManagementService)</span> </span>{
        <span class="hljs-keyword">this</span>.userManagementService = userManagementService;
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">filter</span><span class="hljs-params">(ContainerRequestContext requestContext)</span> <span class="hljs-keyword">throws</span> IOException </span>{
        String authHeader = requestContext.getHeaderString(<span class="hljs-string">"Authorization"</span>);
        <span class="hljs-keyword">if</span> (authHeader == <span class="hljs-keyword">null</span>) {
            <span class="hljs-keyword">return</span>;
        }
        authHeader = authHeader.trim();
        <span class="hljs-keyword">if</span> (!authHeader.startsWith(<span class="hljs-string">"Bearer"</span>)) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> AuthenticationException(<span class="hljs-string">"invalid authentication scheme - please use a Bearer token"</span>);
        }

        String authToken = authHeader.replaceFirst(<span class="hljs-string">"Bearer"</span>, <span class="hljs-string">""</span>).trim();
        Optional&lt;User&gt; user = userManagementService.getUserByAuthToken(authToken);
        user.ifPresent(u -&gt; requestContext.setProperty(<span class="hljs-string">"user"</span>, u));
    }
}
</code></pre>
<p>The full commit for this step of the tutorial can be found <a target="_blank" href="https://github.com/nikist97/TaskManagementService/commit/621510bd1693ef01987a2f73a1eab8c49e2d917d">here</a>.</p>
<h2 id="heading-testing">Testing</h2>
<p>Before we start testing, let's re-build the full stack from scratch:</p>
<pre><code class="lang-bash">docker-compose down
docker-compose up --build -d
</code></pre>
<p>Then, we use the Kratos self-service UI to register a new user by visiting <a target="_blank" href="http://127.0.0.1:4455/welcome">http://127.0.0.1:4455/welcome</a> in the browser.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1693144777110/c2288f42-dbbb-42ea-93f2-9c27e6c81665.png" alt class="image--center mx-auto" /></p>
<p>You will notice that we only need to provide an email address to register because this is what we defined in the user schema. If more fields are added, they will be automatically listed in the registration form we see in this UI.</p>
<p>Choose a username and password and remember them because we will need them later. Keep in mind that Kratos comes in with pre-built password checks by default and won't let you choose a simple password that has already been leaked in data breaches. The email address doesn't matter because emails are only sent locally to mailslurper. For this tutorial I chose:</p>
<ul>
<li><p><em>test@test</em> as email address</p>
</li>
<li><p><em>FMcm6K2Lm9N0KqP</em> as password</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1693144940297/ea4e5def-418f-404b-b007-887073079927.png" alt class="image--center mx-auto" /></p>
<p>After signing up, we should mimic the verification of the email address through mailslurper. This can be done by visiting <a target="_blank" href="http://127.0.0.1:4436/#">http://127.0.0.1:4436/</a> in the browser where you will notice a mailbox-like UI with an email with subject "Please verify your email address"</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1693145131434/945f7f3e-a3ae-429c-8dc4-dc657758e638.png" alt class="image--center mx-auto" /></p>
<p>After opening the email and following the verification link you will be presented with a pre-filled form for finalising the verification.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1693145275189/3c2aeb26-e798-444f-bc5d-2c0c266b4a50.png" alt class="image--center mx-auto" /></p>
<p>After finishing verification, we will be automatically logged in to the self-service UI. You can browse the UI further if you'd like to explore Kratos, but the next step for this tutorial is to head to the terminal and login through an API flow (a Kratos abstraction for a user action - <a target="_blank" href="https://www.ory.sh/docs/kratos/self-service">https://www.ory.sh/docs/kratos/self-service</a>) so that we receive a session token rather than a session cookie which is what happens if we log in through the browser.</p>
<pre><code class="lang-bash">API_LOGIN_FLOW_ID=$(curl -s http://127.0.0.1:4433/self-service/login/api | jq -r <span class="hljs-string">'.id'</span>)
curl -s -X POST -H <span class="hljs-string">'Content-Type: application/json'</span> -H <span class="hljs-string">'Accept: application/json'</span> -d <span class="hljs-string">'{"identifier": "test@test", "password": "FMcm6K2Lm9N0KqP", "method": "password"}'</span> <span class="hljs-string">"http://127.0.0.1:4433/self-service/login?flow=<span class="hljs-variable">$API_LOGIN_FLOW_ID</span>"</span> | jq -r <span class="hljs-string">'.session_token'</span>
ory_st_Q9TNLNgzq9zuucBteAkKXaWubZWOE7wK
</code></pre>
<p>The short script above initializes an API login flow, fetches the ID of the flow, then uses it to login with the credentials chosen above and finally, it fetches the session token from the response.</p>
<p>Now that we have a session token, we can use it to send authenticated requests to the task management service API.</p>
<pre><code class="lang-bash">curl -i -X POST -H <span class="hljs-string">"Authorization: Bearer ory_st_Q9TNLNgzq9zuucBteAkKXaWubZWOE7wK"</span> -H <span class="hljs-string">"Content-Type: application/json"</span> -d <span class="hljs-string">'{"title": "test_title", "description": "test_description"}'</span> -s http://127.0.0.1:8080/api/tasks
HTTP/1.1 201 
Location: http://127.0.0.1:8080/api/tasks/4b47e790-4a63-43b5-8003-c7b50c5b5f2d
Content-Length: 0
Date: Sun, 27 Aug 2023 07:55:54 GMT
</code></pre>
<p>If we then fetch the newly created task, we will see the user ID of the creator.</p>
<pre><code class="lang-bash">curl -s http://127.0.0.1:8080/api/tasks/4b47e790-4a63-43b5-8003-c7b50c5b5f2d | jq <span class="hljs-string">'.'</span>
{
  <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"4b47e790-4a63-43b5-8003-c7b50c5b5f2d"</span>,
  <span class="hljs-string">"title"</span>: <span class="hljs-string">"test_title"</span>,
  <span class="hljs-string">"description"</span>: <span class="hljs-string">"test_description"</span>,
  <span class="hljs-string">"createdBy"</span>: <span class="hljs-string">"45cdab5b-125d-4d31-a1eb-463b816065f2"</span>,
  <span class="hljs-string">"createdAt"</span>: <span class="hljs-string">"2023-08-27T07:55:54.441Z"</span>,
  <span class="hljs-string">"completed"</span>: <span class="hljs-literal">false</span>
}
</code></pre>
<p>We can use the Kratos admin API to check who this user is:</p>
<pre><code class="lang-bash"> curl -s <span class="hljs-string">"http://127.0.0.1:4434/admin/identities/45cdab5b-125d-4d31-a1eb-463b816065f2"</span> | jq <span class="hljs-string">'.'</span>
{
  <span class="hljs-string">"id"</span>: <span class="hljs-string">"45cdab5b-125d-4d31-a1eb-463b816065f2"</span>,
  <span class="hljs-string">"credentials"</span>: {
    <span class="hljs-string">"password"</span>: {
      <span class="hljs-string">"type"</span>: <span class="hljs-string">"password"</span>,
      <span class="hljs-string">"identifiers"</span>: [
        <span class="hljs-string">"test@test"</span>
      ],
      <span class="hljs-string">"version"</span>: 0,
      <span class="hljs-string">"created_at"</span>: <span class="hljs-string">"2023-08-27T07:37:57.12699Z"</span>,
      <span class="hljs-string">"updated_at"</span>: <span class="hljs-string">"2023-08-27T07:53:28.338209Z"</span>
    }
  },
  <span class="hljs-string">"schema_id"</span>: <span class="hljs-string">"user"</span>,
  <span class="hljs-string">"schema_url"</span>: <span class="hljs-string">"http://127.0.0.1:4433/schemas/dXNlcg"</span>,
  <span class="hljs-string">"state"</span>: <span class="hljs-string">"active"</span>,
  <span class="hljs-string">"state_changed_at"</span>: <span class="hljs-string">"2023-08-27T07:37:57.067218753Z"</span>,
  <span class="hljs-string">"traits"</span>: {
    <span class="hljs-string">"email"</span>: <span class="hljs-string">"test@test"</span>
  },
  <span class="hljs-string">"verifiable_addresses"</span>: [
    {
      <span class="hljs-string">"id"</span>: <span class="hljs-string">"caef9cfd-7b97-43d5-b1cd-1d33facd3869"</span>,
      <span class="hljs-string">"value"</span>: <span class="hljs-string">"test@test"</span>,
      <span class="hljs-string">"verified"</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-string">"via"</span>: <span class="hljs-string">"email"</span>,
      <span class="hljs-string">"status"</span>: <span class="hljs-string">"completed"</span>,
      <span class="hljs-string">"verified_at"</span>: <span class="hljs-string">"2023-08-27T07:38:29.563302796Z"</span>,
      <span class="hljs-string">"created_at"</span>: <span class="hljs-string">"2023-08-27T07:37:57.094576Z"</span>,
      <span class="hljs-string">"updated_at"</span>: <span class="hljs-string">"2023-08-27T07:37:57.094576Z"</span>
    }
  ],
  <span class="hljs-string">"recovery_addresses"</span>: [
    {
      <span class="hljs-string">"id"</span>: <span class="hljs-string">"378e3a1f-a0f4-46c8-8d36-0f8fd8ed192e"</span>,
      <span class="hljs-string">"value"</span>: <span class="hljs-string">"test@test"</span>,
      <span class="hljs-string">"via"</span>: <span class="hljs-string">"email"</span>,
      <span class="hljs-string">"created_at"</span>: <span class="hljs-string">"2023-08-27T07:37:57.113134Z"</span>,
      <span class="hljs-string">"updated_at"</span>: <span class="hljs-string">"2023-08-27T07:37:57.113134Z"</span>
    }
  ],
  <span class="hljs-string">"metadata_public"</span>: null,
  <span class="hljs-string">"metadata_admin"</span>: null,
  <span class="hljs-string">"created_at"</span>: <span class="hljs-string">"2023-08-27T07:37:57.070219Z"</span>,
  <span class="hljs-string">"updated_at"</span>: <span class="hljs-string">"2023-08-27T07:37:57.070219Z"</span>
}
</code></pre>
<p>As expected, this user ID corresponds to the user we created earlier as part of this tutorial and with this, our end-to-end manual test is completed.</p>
<h2 id="heading-summary">Summary</h2>
<p>To summarize, in this tutorial we added authentication to the task management API by implementing the following changes:</p>
<ul>
<li><p>we added a <em>createdBy</em> reference to the existing Task entity which represents the ID of the user who created the given task</p>
</li>
<li><p>we added configuration and local deployment setup for <a target="_blank" href="https://www.ory.sh/kratos/">Kratos</a></p>
</li>
<li><p>we added an authentication filter that parses the Authorization HTTP header and calls Kratos API to convert an authentication token to a User entity</p>
</li>
</ul>
<p>We did not cover the Kratos-related abstractions and how Kratos works in general, but I will be happy to go through this in a separate tutorial. For now, my plan for the next post from this series is to introduce a basic level of authorization. The problem we have with the current state of the API is that every user has access to read and update the tasks created by other users. This will be fixed by introducing access control and letting task creators authorize other users to be able to read their tasks if needed.</p>
<p>As always, feel free to ask any questions or leave feedback in the comments section.</p>
]]></content:encoded></item><item><title><![CDATA[The Pyramid Analogy of Progress and Development]]></title><description><![CDATA[The Why
I've been delaying this blog post for a while now because:

it is not strictly related to software engineering which is my career field

it always felt as if I am writing about something that is out of my area


However, throughout this year ...]]></description><link>https://blog.teonibyte.com/the-pyramid-analogy-of-progress-and-development</link><guid isPermaLink="true">https://blog.teonibyte.com/the-pyramid-analogy-of-progress-and-development</guid><category><![CDATA[Self Improvement ]]></category><category><![CDATA[progress]]></category><category><![CDATA[Career]]></category><category><![CDATA[challenge]]></category><category><![CDATA[goals]]></category><dc:creator><![CDATA[Nikolay Stanchev]]></dc:creator><pubDate>Fri, 23 Dec 2022 22:29:03 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/49eeb435d25bad7c8d0a9355c1584d90.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-the-why">The Why</h2>
<p>I've been delaying this blog post for a while now because:</p>
<ul>
<li><p>it is <strong>not</strong> strictly related to software engineering which is my career field</p>
</li>
<li><p>it always felt as if I am writing about something that is out of my area</p>
</li>
</ul>
<p>However, throughout this year I've shared the idea you are going to read about with a few friends and co-workers - to my surprise, all of them reacted positively and (I hope) one way or another it made their career journey easier. That's <strong>why</strong> I decided to finally put these thoughts in a blog post which can reach multiple people that might be struggling or getting discouraged or considering giving up on the goals and path they have chosen for themselves - be it a challenging career, a university degree in a complex field of study, an academic research project, etc. While the examples I give are mainly derived from my journey in software engineering, I have a strong feeling that people with different backgrounds will also share my opinion on this and find the analogy beneficial.</p>
<h2 id="heading-the-what">The What</h2>
<p>We are going to explore <strong>what</strong> I like to call <strong>The Pyramid Analogy of Progress and Development</strong> or in short <strong>The Pyramid Analogy</strong>. Before we dive into it, let's start with a definition of the word analogy from the Cambridge dictionary <a target="_blank" href="https://dictionary.cambridge.org/dictionary/english/analogy">[1]</a>:</p>
<blockquote>
<p>a comparison between things that have similar features, often used to help explain a principle or idea</p>
</blockquote>
<p>This definition explains exactly what I will try to do in this post - I want to explain the idea of perceiving studying and self-development (in any field) as an endless endeavour by comparing it with an exploration journey of an infinite pyramid starting from the top with the goal to cover every inch inside the pyramid.</p>
<p>To explain the analogy, let's introduce a game with the following rules:</p>
<ul>
<li><p>we have an infinitely high pyramid</p>
</li>
<li><p>our starting point is the top of the pyramid</p>
</li>
<li><p>the pyramid is broken down into layers which represent different levels of the game - the more layers we cover, the more we progress</p>
</li>
<li><p>we can only jump from one layer to the next when we have explored the entire surface of the current layer</p>
</li>
</ul>
<p>Here is a diagram that visualizes the game:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671833371831/a9c4220e-ba9b-4874-ace1-fe90bd45b26c.png" alt class="image--center mx-auto" /></p>
<p>The first thing we can notice is that as with many other games the more we progress, the more difficult it becomes to move to the next level. This is because each time we move deeper along the height of the pyramid, we get to a surface area that is larger than the one we have just covered. The second conclusion we can also immediately notice is that this is an infinite game - a game with no end. We can keep making progress by moving from one surface to the next, but because the pyramid is infinitely high, we can never reach the bottom surface. Yet, everyone that has played a similar game knows how addictive it is to keep levelling up and exploring new territories. In the beginning, making progress seems easy. At some point, we start struggling to pass to the next level. Do we feel discouraged when we fail multiple times? Obviously! Do we give up? Of course not! We just keep trying different ways until we succeed - we know we can succeed because otherwise, we wouldn't have been able to pass the previous levels of the game which in this case are the previous layers of the pyramid.</p>
<p>By now, I assume this analogy already sounds familiar. To me, this is exactly how it feels every time I put my focus on a particular goal. In the beginning, it seems easy, my motivation is high, and I think I can achieve the world. The more I progress, the harder it gets. I start feeling discouraged, I want to give up, impostor syndrome kicks in, and motivation is at its lowest levels. In these situations, what we usually fail to remember and understand is that the reason it feels harder is not that we are incompetent or lack the skills to move forward, but rather the fact that we have progressed so deep in the pyramid that the current layer we need to explore is bigger than anything we have seen so far. If we were playing the pyramid exploration game, what we would have done is find a different strategy to pass the new layer - in real life that would mean changing our studying strategy, changing the way we approach our tasks at work, trying out different hacks to be more focused and productive, practising our communication skills, reading more, etc.</p>
<p>Another critical rule of the game that we usually forget in real life is that we cannot skip layers - we can't just jump from layer 1 to layer 10. We need to cover the areas of layer 1, layer 2, layer 3 and so on until we finally reach layer 10. I assume this also sounds familiar to all of us. Frequently, we tend to forget that we cannot skip steps. You cannot just take up a new hobby and become a master of it without enduring the struggles of consistent practice. You cannot just start a new job and expect to learn everything from the company's domain within a month. You cannot demand a promotion without proving your worth. You cannot start a company and turn it into a unicorn startup <a target="_blank" href="https://en.wikipedia.org/wiki/Unicorn_(finance)">[2]</a> without putting in the hard work. All of these are real-life examples of our desires to skip layers in the pyramid game analogy.</p>
<p>Last but not least, another comparison we can draw from this analogy is the infinite nature of the pyramid game. Just as you can keep playing the game and moving from one layer to the next till infinity, the same is true when you start a new career or decide to explore a new area of study or commence anything related to your self-development. We have to remember - it is always an infinite game which gets harder as we progress. However, it is critical to acknowledge that just because the game doesn't end and there will always be something new to learn or to improve, it doesn't mean that we should not pursue the journey. Just the opposite, its infinite nature is what makes it so exciting and what keeps people engaged and helps them overcome the continuous struggle that's part of progressing in the pyramid. As many other (much more successful than me) people like to say - you have to fall in love with the journey rather than the destination. In my experience, the easiest way to achieve that mindset is to embrace <strong>The Pyramid Analogy.</strong></p>
<h2 id="heading-the-how">The How</h2>
<p>So <strong>how</strong> can all of this help you? It's all about changing the perspective we have on challenges encountered during our journey to success for a particular goal. First of all, let's face it, it's inevitable - at a certain point in time, we are all going to face problems, feel down and discouraged, and think of giving up. In these situations, it's easy to go into a spiral of negative thinking where a single negative thought triggers many more, and from there on the domino-effect kicks in. This is when <strong>The Pyramid Analogy</strong> has helped immensely in my personal life - it helped me redirect my mindset in the right direction.</p>
<p>Let me explain the thought process. The pyramid game showed us that the reason overcoming a particular layer turns out to be challenging is the fact that we have covered many other smaller layers before that. So applying this logic in real life, we wouldn't even be facing a particular challenge if we haven't faced and successfully overcome many others before that. So instead of letting our minds go into the spiral of negative thinking, we can re-focus on all the wins we've had in the past - yes, smaller in size in comparison to what we want to achieve right now, but at that time being the biggest win we could get. We can also embrace the fact that because we are covering a wider area in the pyramid right now, it is only logical we would need to put much more effort into it. Just as it is with other games, we search for different approaches to progress to the next level. The motivation and excitement come from the fact that we can keep progressing till infinity - there is always something new we need to learn or another challenge we have to face.</p>
<h2 id="heading-the-examples">The Examples</h2>
<p>To convince you that this is not just a big-words article, I'd like to also present a couple of examples from my personal life (there are plenty of other examples, but I do not want this post to be a this-was-my-life essay):</p>
<ul>
<li><p>Studying in university - for every single subject I decided to put my focus on I could see a clear pyramid analogy; at first, everything seemed easy, but the more you progress, the harder it gets to grasp the new concepts and theories</p>
</li>
<li><p>Software engineering career - probably the most applicable example is my career development in software engineering at <a target="_blank" href="http://www.it-innovation.soton.ac.uk/">The IT Innovation Centre</a> and <a target="_blank" href="https://www.amazon.jobs/en/teams/ascs">Amazon</a>. Not only did I need to acquire more skills to keep progressing in my career, but I also had to continuously learn more and more about the domain of the project I was working on at a given point in time. For both companies, I was experiencing the same pattern - with each new layer of domain knowledge I passed, I only realized how little I knew and that the new layer I had to cover was much wider.</p>
</li>
</ul>
<p>In both examples, I could easily let myself drown in negativity, but instead, I chose to focus on what I did to reach the current level, and embrace that it's only natural for the current challenge to be more difficult - I was covering a larger surface area in the pyramid in comparison to what I have seen before.</p>
<h2 id="heading-the-conclusions">The Conclusions</h2>
<p>If we have to summarize <strong>The Pyramid Analogy</strong>, I would list the following key points:</p>
<ul>
<li><p>Any long-term goals we have can be modelled as an infinite pyramid which needs to be covered in a top-down approach.</p>
</li>
<li><p>As we progress in the infinite pyramid, it is only natural that it's going to feel more challenging - the surface area at a given layer is larger than the areas covered in previous layers.</p>
</li>
<li><p>The journey never ends - we will always have a new surface to explore as we go along the height of the pyramid.</p>
</li>
</ul>
<p>To use this analogy to our advantage, we have to remember that:</p>
<ul>
<li><p>We would have never reached a given layer if we haven't successfully passed other layers before that.</p>
</li>
<li><p>To cover new layers of the pyramid we have to be flexible to changes and always adapt our approach to the current challenge - it's always something we haven't seen, therefore, we cannot expect previous approaches to just work like magic.</p>
</li>
<li><p>Each new layer is the key to much more exciting challenges and opportunities.</p>
</li>
</ul>
<p>I sincerely hope that reading this article will to some extent help you continue your personal journey to success. I would also love to hear any comments you have about the idea presented in this post or examples of your own which resonate with <strong>The Pyramid Analogy.</strong></p>
<h2 id="heading-the-references">The References</h2>
<p>[1] <a target="_blank" href="https://dictionary.cambridge.org/dictionary/english/analogy">https://dictionary.cambridge.org/dictionary/english/analogy</a></p>
<p>[2] <a target="_blank" href="https://en.wikipedia.org/wiki/Unicorn_(finance)">https://en.wikipedia.org/wiki/Unicorn_(finance)</a></p>
]]></content:encoded></item><item><title><![CDATA[Adding Logging and Metrics to a Java REST API]]></title><description><![CDATA[Introduction
This post is a follow-up to the last two tutorials for building a Java REST API (Task Management Service) and integrating it with a MongoDB database:

https://blog.teonisoftware.com/step-by-step-tutorial-for-building-a-rest-api-in-java

...]]></description><link>https://blog.teonibyte.com/adding-logging-and-metrics-to-a-java-rest-api</link><guid isPermaLink="true">https://blog.teonibyte.com/adding-logging-and-metrics-to-a-java-rest-api</guid><category><![CDATA[Java]]></category><category><![CDATA[#prometheus]]></category><category><![CDATA[log4j2]]></category><category><![CDATA[log4j]]></category><category><![CDATA[metrics]]></category><dc:creator><![CDATA[Nikolay Stanchev]]></dc:creator><pubDate>Sat, 17 Dec 2022 23:09:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1671212723407/_S3dD5l_P.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>This post is a follow-up to the last two tutorials for building a Java REST API (Task Management Service) and integrating it with a MongoDB database:</p>
<ul>
<li><p><a target="_blank" href="https://blog.teonisoftware.com/step-by-step-tutorial-for-building-a-rest-api-in-java">https://blog.teonisoftware.com/step-by-step-tutorial-for-building-a-rest-api-in-java</a></p>
</li>
<li><p><a target="_blank" href="https://blog.teonisoftware.com/integrating-a-java-rest-api-with-a-database">https://blog.teonisoftware.com/integrating-a-java-rest-api-with-a-database</a></p>
</li>
</ul>
<p>I've decided to add logging and monitoring functionalities to be able to track the performance of the API. Metrics and logs are two critical parts of achieving operational stability for a given software service. While they serve slightly different purposes and can be used independently, it's usually the combination of the two that gives the greatest benefit when it comes to preventing, troubleshooting and resolving service outages. In this tutorial, we are going to use:</p>
<ul>
<li><p>Log4j2 - a Java software framework that alleviates the process of adding logging functionality in a given piece of software</p>
</li>
<li><p>Prometheus - a set of applications used for storing, querying and visualizing real-time metrics</p>
</li>
</ul>
<h2 id="heading-current-state">Current State</h2>
<p>In the current setup, the local deployment consists of two containers:</p>
<ul>
<li><p>one for running the service for managing TODO/task items</p>
</li>
<li><p>one for running the Mongo DB instance used as the storage mechanism for the API</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671220713366/ZXS9z4hh_.png" alt class="image--center mx-auto" /></p>
<p>Docker is used as the container runtime environment and docker-compose is used to orchestrate the multi-container deployment.</p>
<h2 id="heading-target-state">Target State</h2>
<p>What we want to achieve at the end of this tutorial is the diagram below. The green colour is used to indicate a new component or piece of software.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671220722425/5J1C-ZacQ.png" alt class="image--center mx-auto" /></p>
<p>As we can see, there are 3 major changes:</p>
<ul>
<li><p>A log file where the Task Management Service is going to write information about events, e.g. whenever a new request was processed</p>
</li>
<li><p>A new API endpoint that will expose Prometheus-format metrics indicating the performance of the Task Management Service, e.g. request latency (how much time it took the service to process a given request)</p>
</li>
<li><p>A new container that runs the Prometheus server as well as a Prometheus Job that periodically pulls metrics from the Task Management Service</p>
</li>
</ul>
<h2 id="heading-adding-logs">Adding Logs</h2>
<p>We start this tutorial by adding logs to the API.</p>
<h3 id="heading-configuring-log4j2">Configuring Log4j2</h3>
<p>The first step is to add Log4j as a dependency in the Maven POM file (notice that we are using Log4j v2 which is why this post is referring to Log4j2 instead of its predecessor library Log4j):</p>
<pre><code class="lang-xml">  <span class="hljs-tag">&lt;<span class="hljs-name">dependencies</span>&gt;</span>
    ...
    <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.logging.log4j<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>log4j-api<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.13.0<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.logging.log4j<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>log4j-core<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.13.0<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
    ...
  <span class="hljs-tag">&lt;/<span class="hljs-name">dependencies</span>&gt;</span>
</code></pre>
<p>We also need a special log4j2 properties file which the framework uses to configure the logger instance we need. For this service, we use a very simple config which creates a logger that writes the messages to a file. Since this is not a Log4j-specific tutorial, for details about the config syntax or semantics please refer to the official Log4j <a target="_blank" href="https://logging.apache.org/log4j/2.x/">documentation</a></p>
<pre><code class="lang-ini"><span class="hljs-comment"># logging level and dest for internal events within log4j2</span>
<span class="hljs-attr">status</span> = error
<span class="hljs-attr">dest</span> = err
<span class="hljs-comment"># config name</span>
<span class="hljs-attr">name</span> = ServiceLogConfig

<span class="hljs-comment"># file appender</span>
<span class="hljs-attr">appender.file.type</span> = File
<span class="hljs-attr">appender.file.name</span> = FileLog
<span class="hljs-attr">appender.file.fileName</span> = /var/log/task-management-service/service.log
<span class="hljs-attr">appender.file.layout.type</span> = PatternLayout
<span class="hljs-attr">appender.file.layout.pattern</span> = %d %p %C [%t] %m%n

<span class="hljs-comment"># service-specific logger (the name should be the root package path of all classes that will use this logger)</span>
<span class="hljs-attr">logger.service.name</span> = taskmanagement
<span class="hljs-attr">logger.service.level</span> = info
<span class="hljs-comment"># linking the service-specific logger to the file appender</span>
<span class="hljs-attr">logger.service.appenderRefs</span> = file
<span class="hljs-attr">logger.service.appenderRef.file.ref</span> = FileLog
</code></pre>
<p>The full commit for this step can be found <a target="_blank" href="https://github.com/nikist97/TaskManagementService/commit/235f46727098dfcb699128fb90406770896bf5e9">here</a>.</p>
<h3 id="heading-writing-logs">Writing logs</h3>
<p>Now that we have Log4j2 configured we can start logging messages from within the service. For example, we can override the `toString()` method of the Task class and log a message whenever a new task is created that would show us the auto-generated ID of the new task:</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Task</span> </span>{
    ...
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">toString</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Task{"</span> +
                <span class="hljs-string">"identifier='"</span> + identifier + <span class="hljs-string">'\''</span> +
                <span class="hljs-string">", title='"</span> + title + <span class="hljs-string">'\''</span> +
                <span class="hljs-string">", description='"</span> + description + <span class="hljs-string">'\''</span> +
                <span class="hljs-string">", createdAt="</span> + createdAt +
                <span class="hljs-string">", completed="</span> + completed +
                <span class="hljs-string">'}'</span>;
    }
    ...
}
</code></pre>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TaskManagementService</span> </span>{

    <span class="hljs-comment">// get a logger instance for this class</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Logger LOGGER = LogManager.getLogger(TaskManagementService.class);

    ...

    <span class="hljs-function"><span class="hljs-keyword">public</span> Task <span class="hljs-title">create</span><span class="hljs-params">(String title, String description)</span>  </span>{
        Task task = Task.builder(title, description).build();

        repository.save(task);

        <span class="hljs-comment">// log an info message for each new task that was created</span>
        LOGGER.info(<span class="hljs-string">"Successfully created new task {}"</span>, task);

        <span class="hljs-keyword">return</span> task;
    }
...
}
</code></pre>
<p>The full commit for this step can be found <a target="_blank" href="https://github.com/nikist97/TaskManagementService/commit/2f685aebbba87b475738fea8fe31ddd2ba3ed7dc">here</a>.</p>
<p>Another useful thing we can log is a message for each new API request that is sent to the service - the message will contain the request method, the request URI, the response status code and the processing time of the request. To achieve this, we need a LoggingFilter class that implements both the container request filter interface and the container response filter interface:</p>
<pre><code class="lang-java"><span class="hljs-meta">@Provider</span>
<span class="hljs-meta">@PreMatching</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LoggingFilter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ContainerRequestFilter</span>, <span class="hljs-title">ContainerResponseFilter</span> </span>{

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Logger LOGGER = LogManager.getLogger(LoggingFilter.class);

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">filter</span><span class="hljs-params">(ContainerRequestContext requestContext)</span> </span>{
        <span class="hljs-keyword">long</span> processingStartTime = Instant.now().toEpochMilli();
        requestContext.setProperty(<span class="hljs-string">"requestProcessingStartTime"</span>, processingStartTime);
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">filter</span><span class="hljs-params">(ContainerRequestContext requestContext, ContainerResponseContext responseContext)</span> </span>{
        <span class="hljs-keyword">long</span> processingStartTime = (<span class="hljs-keyword">long</span>) requestContext.getProperty(<span class="hljs-string">"requestProcessingStartTime"</span>);
        <span class="hljs-keyword">long</span> processingEndTime = Instant.now().toEpochMilli();
        <span class="hljs-keyword">long</span> processingLatency = processingEndTime - processingStartTime;

        LOGGER.info(<span class="hljs-string">"{} {} {} {}ms"</span>,
                requestContext.getMethod(),
                requestContext.getUriInfo().getRequestUri().getPath(),
                responseContext.getStatus(),
                processingLatency);
    }
}
</code></pre>
<p>The first filter method captures the request before it has been processed by a resource class and records the request processing start time. The second filter method captures the request after it has been processed and calculates how many milliseconds it took the service to process the request. It is important to note that we use the <code>@PreMatching</code> annotation to ensure that the filter is executed even when the Jersey framework doesn't find an appropriate resource class that corresponds to the request URI (i.e. whenever we have a 404 response from the service). As with other provider classes we used in the previous tutorials, we bind the <code>LoggingFilter</code> in the application config:</p>
<pre><code class="lang-java"><span class="hljs-meta">@ApplicationPath("/api")</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ApplicationConfig</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ResourceConfig</span> </span>{

    <span class="hljs-meta">@Inject</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">ApplicationConfig</span><span class="hljs-params">(ServiceLocator serviceLocator)</span> </span>{
        ...
        register(LoggingFilter.class);
        ...
    }

}
</code></pre>
<p>The full commit for this step can be found <a target="_blank" href="https://github.com/nikist97/TaskManagementService/commit/5db4c13ddff7d69bb5e043abfefdc6f1138f4553">here</a>.</p>
<h3 id="heading-inspecting-logs">Inspecting Logs</h3>
<p>To test that the service is logging messages, we can inspect the log file we configured in the log4j2 config file. First, we start the local deployment of the application and send a couple of test requests to create new tasks.</p>
<pre><code class="lang-bash">docker-compose up --build -d

curl -i -X POST -H <span class="hljs-string">"Content-Type:application/json"</span> -d <span class="hljs-string">"{\"title\": \"test-title-1\", \"description\":\"description\"}"</span> <span class="hljs-string">"http://localhost:8080/api/tasks"</span> 
HTTP/1.1 201 
Location: http://localhost:8080/api/tasks/9e3ce938-1206-480d-b84a-0f782eaeb0b4
Content-Length: 0
Date: Sat, 17 Dec 2022 10:46:57 GMT

curl -i -X POST -H <span class="hljs-string">"Content-Type:application/json"</span> -d <span class="hljs-string">"{\"title\": \"test-title-2\", \"description\":\"description\"}"</span> <span class="hljs-string">"http://localhost:8080/api/tasks"</span> 
HTTP/1.1 201 
Location: http://localhost:8080/api/tasks/ccdc375a-a278-4416-826d-62cbbd723b42
Content-Length: 0
Date: Sat, 17 Dec 2022 10:47:09 GMT
</code></pre>
<p>Then, we inspect the log file in the docker container using the <code>docker exec</code> command.</p>
<pre><code class="lang-bash">docker <span class="hljs-built_in">exec</span> -t taskmanagementservice_api sh -c <span class="hljs-string">"cat /var/log/task-management-service/service.log"</span>

2022-12-17 10:46:57,143 INFO taskmanagement.service.TaskManagementService [http-nio-8080-exec-1] Successfully created new task Task{identifier=<span class="hljs-string">'9e3ce938-1206-480d-b84a-0f782eaeb0b4'</span>, title=<span class="hljs-string">'test-title-1'</span>, description=<span class="hljs-string">'description'</span>, createdAt=2022-12-17T10:46:57.100681Z, completed=<span class="hljs-literal">false</span>}

2022-12-17 10:46:57,156 INFO taskmanagement.api.LoggingFilter [http-nio-8080-exec-1] POST /api/tasks 201 164ms

2022-12-17 10:47:09,249 INFO taskmanagement.service.TaskManagementService [http-nio-8080-exec-3] Successfully created new task Task{identifier=<span class="hljs-string">'ccdc375a-a278-4416-826d-62cbbd723b42'</span>, title=<span class="hljs-string">'test-title-2'</span>, description=<span class="hljs-string">'description'</span>, createdAt=2022-12-17T10:47:09.244510Z, completed=<span class="hljs-literal">false</span>}

2022-12-17 10:47:09,250 INFO taskmanagement.api.LoggingFilter [http-nio-8080-exec-3] POST /api/tasks 201 8ms
</code></pre>
<p>As we can see, for each request we have two messages:</p>
<ul>
<li><p>one from the TaskManagementService class which indicates the creation of a new task</p>
</li>
<li><p>one from the LoggingFilter showing the response status code and request processing time</p>
</li>
</ul>
<h2 id="heading-adding-metrics">Adding Metrics</h2>
<p>The next topic of this tutorial is adding metrics to our API. This is a more complex topic, but I tried to bring it down to a few small and easily digestible steps.</p>
<p>Now some background information on Prometheus. It consists of many components of which a major one is the Prometheus server. This component is responsible for scraping, storing and querying the metrics data. For this tutorial, we are going to run the Prometheus server inside a new Docker container. When it comes to metrics data collection, Prometheus follows an HTTP pull model or in other words, the Prometheus server periodically queries other applications for their metrics data. On a high level, this is how it works:</p>
<ul>
<li><p>We configure jobs on the Prometheus server.</p>
</li>
<li><p>Each job is responsible for collecting the metrics from a particular piece of software (e.g. Nginx)</p>
</li>
<li><p>Each job can query multiple targets (e.g. multiple hosts running Nginx)</p>
</li>
<li><p>The query itself represents a GET HTTP call to each target using the URL path <code>/metrics</code> (this is configurable and can be changed for individual jobs)</p>
</li>
<li><p>If needed different authorization mechanisms can be used in the HTTP call - e.g. providing a Bearer token</p>
</li>
</ul>
<p>For more Prometheus-specific information, please refer to the overview <a target="_blank" href="https://prometheus.io/docs/introduction/overview">documentation</a>.</p>
<h3 id="heading-prometheus-container-setup">Prometheus Container Setup</h3>
<p>The first step for integration with Prometheus is to include a Prometheus container in the docker-compose deployment file:</p>
<pre><code class="lang-yaml">  <span class="hljs-attr">prometheus:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">"prom/prometheus:v2.40.7"</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">taskmanagementservice_prometheus</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">.data/prometheus:/prometheus</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./prometheus.yml:/etc/prometheus/prometheus.yml</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"9090:9090"</span>
</code></pre>
<p>Two key points in the configuration above:</p>
<ul>
<li><p>We mount a persistent volume to the Prometheus container so that metrics are not lost when the container crashes or is restarted - any data stored in <code>/prometheus</code> (default location where Prometheus stores data) on the container will be stored under <code>.data/prometheus</code> (relative to the project root folder) on your local machine.</p>
</li>
<li><p>We do the same for the <code>prometheus.yml</code> config file. <code>/etc/prometheus/prometheus.yml</code> is the default location where the Prometheus server will look for a configuration file on startup.</p>
</li>
</ul>
<p>As for the Prometheus config file itself, for now, we are going to use a very simple config that instructs Prometheus to scrape itself (this means it will query its own <code>/metrics</code> API endpoint). The scrape interval for all jobs is set to 15 seconds.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">global:</span>
  <span class="hljs-attr">scrape_interval:</span> <span class="hljs-string">15s</span>

<span class="hljs-attr">scrape_configs:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">job_name:</span> <span class="hljs-string">'prometheus'</span>

    <span class="hljs-attr">static_configs:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">targets:</span> [<span class="hljs-string">'localhost:9090'</span>]
</code></pre>
<p>The full commit for this step can be found <a target="_blank" href="https://github.com/nikist97/TaskManagementService/commit/3f79c5928006dfb784211e08db6aaf8825b693c5">here</a>.</p>
<h3 id="heading-visualizing-prometheus-metrics">Visualizing Prometheus Metrics</h3>
<p>In a production environment, most likely we would have been using a more advanced dashboarding solution (e.g. Grafana) to visualize metrics. However, in this case, we are going to use the Prometheus server built-in expression browser. One metric that Prometheus exposes about itself is called <code>prometheus_http_requests_total</code> and it represents a counter of the number of HTTP requests sent to the Prometheus server broken down by status code, URL path, etc. An example graph for this metric can be seen by visiting this <a target="_blank" href="https://tinyurl.com/2trkn8rm">URL</a> in your browser (assuming the local deployment is up and running).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671288675093/njrpQEpds.png" alt class="image--center mx-auto" /></p>
<p>What this graph represents is the counter for successful requests sent to the <code>/metrics</code> URL path. As expected, this is a continuously increasing graph because the metrics endpoint is continuously queried by the job we configured for collecting metrics from the Prometheus server itself.</p>
<h3 id="heading-implementing-service-metrics-api-endpoint">Implementing Service Metrics API Endpoint</h3>
<p>The next step is to implement a metrics API endpoint in the Task Management Service. This endpoint will be queried by a Prometheus metrics collection job that we will configure later.</p>
<p>We start by adding a dependency in the POM file for the Prometheus client library:</p>
<pre><code class="lang-xml">  <span class="hljs-tag">&lt;<span class="hljs-name">dependencies</span>&gt;</span>
    ...
    <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>io.prometheus<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>simpleclient_httpserver<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>0.16.0<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
    ...
  <span class="hljs-tag">&lt;/<span class="hljs-name">dependencies</span>&gt;</span>
</code></pre>
<p>Then, we implement the metrics API resource class:</p>
<pre><code class="lang-java"><span class="hljs-meta">@Path("/metrics")</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MetricsResource</span> </span>{

    <span class="hljs-meta">@GET</span>
    <span class="hljs-meta">@Produces(MediaType.TEXT_PLAIN)</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">metrics</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> IOException </span>{
        StringWriter writer = <span class="hljs-keyword">new</span> StringWriter();

        TextFormat.write004(writer, CollectorRegistry.defaultRegistry.metricFamilySamples());

        <span class="hljs-keyword">return</span> writer.toString();
    }

}
</code></pre>
<p>It is worth mentioning that throughout this implementation we are going to use the default metrics collectors registry which is sufficient for our use case and also makes the implementation much simpler. As we can see in the code above, the implementation of the endpoint is simply calling a method on the default registry and returning the response as plain text. The Prometheus client library outputs the response in the format expected by the Prometheus server job. (We will see what the format looks like during testing).</p>
<p>Next, we implement the metrics filter (the class responsible for populating metrics in the default registry), but before that, we need to make two small changes in the logging filter we implemented earlier:</p>
<ul>
<li><p>adding a priority annotation to define the order in which the filters are executed (the lower the number, the higher the priority)</p>
</li>
<li><p>setting a request context property - <code>requestProcessingTime</code> - to preserve the request processing time so that it can also be used in the metrics filter</p>
</li>
</ul>
<pre><code class="lang-java"><span class="hljs-meta">@Priority(2)</span>
<span class="hljs-meta">@Provider</span>
<span class="hljs-meta">@PreMatching</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LoggingFilter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ContainerRequestFilter</span>, <span class="hljs-title">ContainerResponseFilter</span> </span>{
    ...
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">filter</span><span class="hljs-params">(ContainerRequestContext requestContext, ContainerResponseContext responseContext)</span> </span>{
        ...
        <span class="hljs-comment">// save the request processing time so that it can be used in the metrics filter</span>
        requestContext.setProperty(<span class="hljs-string">"requestProcessingTime"</span>, processingLatency);
        ...
    }
}
</code></pre>
<p>As for the metrics filter, this is what it looks like:</p>
<pre><code class="lang-java"><span class="hljs-meta">@Priority(1)</span>
<span class="hljs-meta">@Provider</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MetricsFilter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ContainerResponseFilter</span> </span>{

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> String[] LABELS = <span class="hljs-keyword">new</span> String[]{
            <span class="hljs-string">"requestMethod"</span>,
            <span class="hljs-string">"uriPath"</span>,
            <span class="hljs-string">"responseCode"</span>
    };

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Histogram requestLatencyHistogram = Histogram.build()
            .name(<span class="hljs-string">"task_management_service_requests_latency"</span>)
            .labelNames(LABELS)
            .buckets(<span class="hljs-number">5</span>, <span class="hljs-number">25</span>, <span class="hljs-number">50</span>, <span class="hljs-number">75</span>, <span class="hljs-number">100</span>, <span class="hljs-number">500</span>, <span class="hljs-number">1000</span>, <span class="hljs-number">5000</span>, <span class="hljs-number">10000</span>)
            .help(<span class="hljs-string">"Request latency in milli-seconds."</span>)
            .register();

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">filter</span><span class="hljs-params">(ContainerRequestContext requestContext, ContainerResponseContext responseContext)</span> </span>{
        String[] labelsValues = <span class="hljs-keyword">new</span> String[<span class="hljs-number">3</span>];

        String requestMethod = requestContext.getMethod();
        labelsValues[<span class="hljs-number">0</span>] = requestMethod;

        String uriPath = requestContext.getUriInfo().getRequestUri().getPath();
        labelsValues[<span class="hljs-number">1</span>] = uriPath;

        <span class="hljs-keyword">int</span> responseCode = responseContext.getStatus();
        labelsValues[<span class="hljs-number">2</span>] = Integer.toString(responseCode);

        <span class="hljs-keyword">long</span> processingTime = (<span class="hljs-keyword">long</span>) requestContext.getProperty(<span class="hljs-string">"requestProcessingTime"</span>);

        requestLatencyHistogram
                .labels(labelsValues)
                .observe(processingTime);
    }
}
</code></pre>
<p>Key points here are:</p>
<ul>
<li><p>We use priority with value 1 which is less than the value of the logging filter priority - what this means is that the order of execution would be the following:</p>
<ol>
<li><p>request filter method from MetricsFilter class (if one exists, in our case there isn't one)</p>
</li>
<li><p>request filter method from LoggingFilter class</p>
</li>
<li><p>response filter method from LoggingFilter class</p>
</li>
<li><p>response filter method from MetricsFilter class</p>
</li>
</ol>
</li>
<li><p>We rely on the fact that the logging filter's execution was successful so that the request processing time property can be extracted from the request context</p>
</li>
<li><p>We use a histogram registered in the default registry to collect the latency metric - for more information on histograms, refer to the <a target="_blank" href="https://prometheus.io/docs/practices/histograms/">documentation</a></p>
</li>
</ul>
<p>Finally, we need to register both the MetricsFilter class and the MetricsResource class in the application API config:</p>
<pre><code class="lang-java"><span class="hljs-meta">@ApplicationPath("/api")</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ApplicationConfig</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ResourceConfig</span> </span>{

    <span class="hljs-meta">@Inject</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">ApplicationConfig</span><span class="hljs-params">(ServiceLocator serviceLocator)</span> </span>{
        ...
        register(MetricsResource.class);
        ...
        register(MetricsFilter.class);
        ...
    }

}
</code></pre>
<p>Now, we can test the new endpoint using <code>curl</code>. For a fresh deployment, the first time we query the endpoint we get this response:</p>
<pre><code class="lang-bash">curl -i -X GET  <span class="hljs-string">"http://localhost:8080/api/metrics"</span> 
HTTP/1.1 200 
Content-Type: text/plain
Content-Length: 140
Date: Sat, 17 Dec 2022 17:10:02 GMT

<span class="hljs-comment"># HELP task_management_service_requests_latency Request latency in milli-seconds.</span>
<span class="hljs-comment"># TYPE task_management_service_requests_latency histogram</span>
</code></pre>
<p>This is because this request is the first one processed by the task management service. Therefore, no metrics have been collected yet. If we repeat the same request, we get the following:</p>
<pre><code class="lang-bash">curl -i -X GET  <span class="hljs-string">"http://localhost:8080/api/metrics"</span> 
HTTP/1.1 200 
Content-Type: text/plain
Content-Length: 1918
Date: Sat, 17 Dec 2022 17:11:25 GMT

<span class="hljs-comment"># HELP task_management_service_requests_latency Request latency in milli-seconds.</span>
<span class="hljs-comment"># TYPE task_management_service_requests_latency histogram</span>
task_management_service_requests_latency_bucket{requestMethod=<span class="hljs-string">"GET"</span>,uriPath=<span class="hljs-string">"/api/metrics"</span>,responseCode=<span class="hljs-string">"200"</span>,le=<span class="hljs-string">"5.0"</span>,} 0.0
task_management_service_requests_latency_bucket{requestMethod=<span class="hljs-string">"GET"</span>,uriPath=<span class="hljs-string">"/api/metrics"</span>,responseCode=<span class="hljs-string">"200"</span>,le=<span class="hljs-string">"25.0"</span>,} 1.0
task_management_service_requests_latency_bucket{requestMethod=<span class="hljs-string">"GET"</span>,uriPath=<span class="hljs-string">"/api/metrics"</span>,responseCode=<span class="hljs-string">"200"</span>,le=<span class="hljs-string">"50.0"</span>,} 1.0
task_management_service_requests_latency_bucket{requestMethod=<span class="hljs-string">"GET"</span>,uriPath=<span class="hljs-string">"/api/metrics"</span>,responseCode=<span class="hljs-string">"200"</span>,le=<span class="hljs-string">"75.0"</span>,} 1.0
task_management_service_requests_latency_bucket{requestMethod=<span class="hljs-string">"GET"</span>,uriPath=<span class="hljs-string">"/api/metrics"</span>,responseCode=<span class="hljs-string">"200"</span>,le=<span class="hljs-string">"100.0"</span>,} 1.0
task_management_service_requests_latency_bucket{requestMethod=<span class="hljs-string">"GET"</span>,uriPath=<span class="hljs-string">"/api/metrics"</span>,responseCode=<span class="hljs-string">"200"</span>,le=<span class="hljs-string">"500.0"</span>,} 1.0
task_management_service_requests_latency_bucket{requestMethod=<span class="hljs-string">"GET"</span>,uriPath=<span class="hljs-string">"/api/metrics"</span>,responseCode=<span class="hljs-string">"200"</span>,le=<span class="hljs-string">"1000.0"</span>,} 1.0
task_management_service_requests_latency_bucket{requestMethod=<span class="hljs-string">"GET"</span>,uriPath=<span class="hljs-string">"/api/metrics"</span>,responseCode=<span class="hljs-string">"200"</span>,le=<span class="hljs-string">"5000.0"</span>,} 1.0
task_management_service_requests_latency_bucket{requestMethod=<span class="hljs-string">"GET"</span>,uriPath=<span class="hljs-string">"/api/metrics"</span>,responseCode=<span class="hljs-string">"200"</span>,le=<span class="hljs-string">"10000.0"</span>,} 1.0
task_management_service_requests_latency_bucket{requestMethod=<span class="hljs-string">"GET"</span>,uriPath=<span class="hljs-string">"/api/metrics"</span>,responseCode=<span class="hljs-string">"200"</span>,le=<span class="hljs-string">"+Inf"</span>,} 1.0
task_management_service_requests_latency_count{requestMethod=<span class="hljs-string">"GET"</span>,uriPath=<span class="hljs-string">"/api/metrics"</span>,responseCode=<span class="hljs-string">"200"</span>,} 1.0
task_management_service_requests_latency_sum{requestMethod=<span class="hljs-string">"GET"</span>,uriPath=<span class="hljs-string">"/api/metrics"</span>,responseCode=<span class="hljs-string">"200"</span>,} 7.0
<span class="hljs-comment"># HELP task_management_service_requests_latency_created Request latency in milli-seconds.</span>
<span class="hljs-comment"># TYPE task_management_service_requests_latency_created gauge</span>
task_management_service_requests_latency_created{requestMethod=<span class="hljs-string">"GET"</span>,uriPath=<span class="hljs-string">"/api/metrics"</span>,responseCode=<span class="hljs-string">"200"</span>,} 1.671297002185E9
</code></pre>
<p>It's visible that this is a very robust response because of the multiple histogram buckets. However, all it is saying is that there was one processed request in total (the first curl request we sent) and it took 7ms to process it (the histogram works based on a list of buckets and a metric is recorded in all buckets with a value bigger than the metric value - that's why only the first bucket with value 5 still has a count of 0)</p>
<p>We can also confirm the above by looking at the log file again which shows that the request took 7ms to process:</p>
<pre><code class="lang-bash">docker <span class="hljs-built_in">exec</span> -t taskmanagementservice_api sh -c <span class="hljs-string">"cat /var/log/task-management-service/service.log"</span>
2022-12-17 17:10:02,182 INFO taskmanagement.api.LoggingFilter [http-nio-8080-exec-1] GET /api/metrics 200 7ms
</code></pre>
<p>Now we need to integrate Prometheus with this endpoint so that service metrics are continuously collected. This can be achieved by configuring a new job in the Prometheus config file:</p>
<pre><code class="lang-yaml"><span class="hljs-string">...</span>
<span class="hljs-attr">scrape_configs:</span> 
  <span class="hljs-bullet">-</span> <span class="hljs-attr">job_name:</span> <span class="hljs-string">'task-management-service'</span>

    <span class="hljs-attr">metrics_path:</span> <span class="hljs-string">/api/metrics</span>

    <span class="hljs-attr">static_configs:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">targets:</span> [<span class="hljs-string">'localhost:8080'</span>]
<span class="hljs-string">...</span>
</code></pre>
<p>The full commit for this step can be found <a target="_blank" href="https://github.com/nikist97/TaskManagementService/commit/1efefc8bc3274339f178c2f354b304e9adf707a3">here</a>.</p>
<p>Finally, we reset the deployment so that the new changes can take effect:</p>
<pre><code class="lang-bash">docker-compose down
docker-compose up --build -d
</code></pre>
<h3 id="heading-visualizing-service-metrics">Visualizing Service Metrics</h3>
<p>To see the service-specific metrics visualized in the Prometheus expression browser, we need to send a few test requests first:</p>
<pre><code class="lang-bash">curl -i -X POST -H <span class="hljs-string">"Content-Type:application/json"</span> -d <span class="hljs-string">"{\"title\": \"test-title-1\", \"description\":\"description\"}"</span> <span class="hljs-string">"http://localhost:8080/api/tasks"</span>
HTTP/1.1 201 
Location: http://localhost:8080/api/tasks/ae33f988-0260-498b-b9c6-51d52250ffce
Content-Length: 0
Date: Sat, 17 Dec 2022 17:56:46 GMT

curl -i -X GET <span class="hljs-string">"http://localhost:8080/api/tasks/ae33f988-0260-498b-b9c6-51d52250ffce"</span>
HTTP/1.1 200 
Content-Type: application/json
Content-Length: 161
Date: Sat, 17 Dec 2022 17:57:17 GMT

{<span class="hljs-string">"identifier"</span>:<span class="hljs-string">"ae33f988-0260-498b-b9c6-51d52250ffce"</span>,<span class="hljs-string">"title"</span>:<span class="hljs-string">"test-title-1"</span>,<span class="hljs-string">"description"</span>:<span class="hljs-string">"description"</span>,<span class="hljs-string">"createdAt"</span>:<span class="hljs-string">"2022-12-17T17:56:46.856Z"</span>,<span class="hljs-string">"completed"</span>:<span class="hljs-literal">false</span>}  

curl -i -X POST -H <span class="hljs-string">"Content-Type:application/json"</span> -d <span class="hljs-string">"{\"title\": \"test-title-1\", \"description\":\"description\"}"</span> <span class="hljs-string">"http://localhost:8080/api/tasks"</span>
HTTP/1.1 201 
Location: http://localhost:8080/api/tasks/b373370c-1abe-410f-a646-d1bab895cd96
Content-Length: 0
Date: Sat, 17 Dec 2022 17:59:18 GMT

curl -i -X GET <span class="hljs-string">"http://localhost:8080/api/tasks/b373370c-1abe-410f-a646-d1bab895cd96"</span>
HTTP/1.1 200 
Content-Type: application/json
Content-Length: 161
Date: Sat, 17 Dec 2022 17:59:30 GMT

{<span class="hljs-string">"identifier"</span>:<span class="hljs-string">"b373370c-1abe-410f-a646-d1bab895cd96"</span>,<span class="hljs-string">"title"</span>:<span class="hljs-string">"test-title-1"</span>,<span class="hljs-string">"description"</span>:<span class="hljs-string">"description"</span>,<span class="hljs-string">"createdAt"</span>:<span class="hljs-string">"2022-12-17T17:59:18.181Z"</span>,<span class="hljs-string">"completed"</span>:<span class="hljs-literal">false</span>}
</code></pre>
<p>After waiting for a minute, we should be able to see the new metrics in the Prometheus expression browser by going to <a target="_blank" href="http://localhost:9090/graph">http://localhost:9090/graph</a> and searching for the <code>task_management_service_requests_latency_count</code> metric. In the table view we can see the number of processed requests broken down by URI path, request method and response status code:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671300281307/t-Nl5ObUk.png" alt class="image--center mx-auto" /></p>
<p>If you are interested in playing around with the new metric, in the Prometheus histograms <a target="_blank" href="https://prometheus.io/docs/practices/histograms/">documentation</a> you will find other queries that can be used to produce valuable performance graphs.</p>
<h2 id="heading-summary">Summary</h2>
<p>In conclusion, as part of this tutorial, we have implemented two major operational stability improvements in the task management service API:</p>
<ol>
<li><p>file-based logging functionality using log4j2 which helped us:</p>
<ul>
<li><p>log a message whenever a new task is created so that we can easily find the auto-generated UUID for the new task</p>
</li>
<li><p>log a message whenever a new request is processed so that we can easily find the response code for the given request as well the time it took to process the request</p>
</li>
</ul>
</li>
<li><p>metrics functionality using Prometheus which helped us:</p>
<ul>
<li>emit metrics for the latency and count of processed API requests broken down by URI path, request method and response status code</li>
</ul>
</li>
</ol>
<p>We also visualized the new metrics in the Prometheus expression browser. Ideally, we can integrate <a target="_blank" href="https://grafana.com/">Grafana</a> with Prometheus so that these metrics are included in a proper monitoring dashboard. Let me know if you would like to see a separate tutorial on this or if you have any questions regarding the topics explored in this blog post.</p>
]]></content:encoded></item><item><title><![CDATA[Integrating a Java REST API With a Database]]></title><description><![CDATA[Introduction
This article is a follow-up to my last tutorial on building a fully functional Java REST API for managing TODO tasks. For simplicity, last time we used an in-memory database as an implementation of the storage interface defined by our bu...]]></description><link>https://blog.teonibyte.com/integrating-a-java-rest-api-with-a-database</link><guid isPermaLink="true">https://blog.teonibyte.com/integrating-a-java-rest-api-with-a-database</guid><category><![CDATA[Java]]></category><category><![CDATA[Databases]]></category><category><![CDATA[MongoDB]]></category><category><![CDATA[REST API]]></category><category><![CDATA[Docker]]></category><dc:creator><![CDATA[Nikolay Stanchev]]></dc:creator><pubDate>Sun, 31 Jul 2022 15:35:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1670532130692/KCR3U-0cT.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>This article is a follow-up to my last <a target="_blank" href="https://nsnotes.hashnode.dev/step-by-step-tutorial-for-building-a-rest-api-in-java">tutorial</a> on building a fully functional Java REST API for managing TODO tasks. For simplicity, last time we used an in-memory database as an implementation of the storage interface defined by our business logic. It is clear that this solution is not feasible for a production-ready API mainly because:</p>
<ul>
<li><p>we will lose the stored tasks every time we stop the application</p>
</li>
<li><p>different application instances will not be able to share memory - in other words, if we horizontally scale up our service by using two servers - A and B, then tasks created through server A will not be seen by tasks created through server B.</p>
</li>
</ul>
<p>This is illustrated in the diagram below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659095308914/IXVI2Eq3t.png" alt="Screenshot 2022-07-29 at 14.47.59.png" /></p>
<p>In this tutorial I want to demonstrate how we can integrate the API we built as part of the previous <a target="_blank" href="https://nsnotes.hashnode.dev/step-by-step-tutorial-for-building-a-rest-api-in-java">article</a> with a very famous open-source non-relational database, namely MongoDB. This means changing the diagram above to the following:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659095508226/BJRMFnNot.png" alt="Screenshot 2022-07-29 at 14.51.16.png" /></p>
<p>For the purpose of simplicity, I chose MongoDB, but technically we could have also chosen a relational database such as PostgreSQL. If we were to choose between the two DBs, we would need to know a bit more about the software requirements of our service - things like:</p>
<ul>
<li><p>scalability - how much is the application expected to grow in the future</p>
</li>
<li><p>expected traffic pattern - how many clients are going to use the service, what request rate they will use, read vs write ratio, etc.</p>
</li>
<li><p>access patterns - do we have a well-defined access pattern requested by our business stakeholders or do we need an approach that allows for general search queries</p>
</li>
</ul>
<p>The API specification is not enough on its own to make this decision, but for the purpose of this tutorial, we can go ahead and use MongoDB.</p>
<h2 id="heading-current-state">Current State</h2>
<p>A quick reminder of what we built before is given below:</p>
<ul>
<li>a repository interface defining the functionality our business logic requires for storing and retrieving tasks</li>
</ul>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">TaskManagementRepository</span> </span>{

    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">save</span><span class="hljs-params">(Task task)</span></span>;

    <span class="hljs-function">List&lt;Task&gt; <span class="hljs-title">getAll</span><span class="hljs-params">()</span></span>;

    <span class="hljs-function">Optional&lt;Task&gt; <span class="hljs-title">get</span><span class="hljs-params">(String taskID)</span></span>;

    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">delete</span><span class="hljs-params">(String taskID)</span></span>;
}
</code></pre>
<ul>
<li>an in-memory DB implementation of the repository interface that uses a hash map to store and retrieve tasks</li>
</ul>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">InMemoryTaskManagementRepository</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">TaskManagementRepository</span> </span>{

    <span class="hljs-keyword">private</span> Map&lt;String, Task&gt; tasks = <span class="hljs-keyword">new</span> HashMap&lt;&gt;();

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">save</span><span class="hljs-params">(Task task)</span> </span>{
        tasks.put(task.getIdentifier(), task);
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> List&lt;Task&gt; <span class="hljs-title">getAll</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> tasks.values().stream()
                .collect(Collectors.toUnmodifiableList());
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Optional&lt;Task&gt; <span class="hljs-title">get</span><span class="hljs-params">(String taskID)</span> </span>{
        <span class="hljs-keyword">return</span> Optional.ofNullable(tasks.get(taskID));
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">delete</span><span class="hljs-params">(String taskID)</span> </span>{
        tasks.remove(taskID);
    }

}
</code></pre>
<ul>
<li>a Guice binding that ensures every piece of business logic that needs to use a <strong>TaskManagementRepository</strong> instance will use the same <strong>InMemoryTaskManagementRepository</strong> instance</li>
</ul>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ApplicationModule</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractModule</span> </span>{

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">configure</span><span class="hljs-params">()</span> </span>{
        bind(TaskManagementRepository.class).to(InMemoryTaskManagementRepository.class).in(Singleton.class);
    }

}
</code></pre>
<h2 id="heading-target-state">Target State</h2>
<p>The goal of this tutorial is to implement a new class called <strong>MongoDBTaskManagementRepository</strong> which will be an implementation of the repository interface that uses MongoDB for persistent storage.</p>
<p>We are going to have the DB running as a separate Docker container. Therefore, we will now have an application made of two containers - the service container and the DB container. We will use <a target="_blank" href="https://docs.docker.com/compose/">Docker Compose</a> to manage the multi-container application as a single entity. Keep in mind that this is not a Docker-focused tutorial, so the focus won't be on explaining best practices for setting up a database with Docker or how to use Docker and Docker Compose in general. Feel free to leave a comment if you would like to see a separate article for this.</p>
<h2 id="heading-database-integration">Database Integration</h2>
<h3 id="heading-mongodb-container-setup">MongoDB Container Setup</h3>
<p>We start by pulling a MongoDB Docker image and setting up our Docker Compose file to start two containers - one for the service itself and one for a MongoDB instance:</p>
<pre><code class="lang-bash">docker pull mongo:5.0.9
</code></pre>
<p>The Docker Compose file is given below. Notice that we mount a persistent volume to the MongoDB container since we don't want the data to be wiped out whenever the container crashes or is rebuilt). What this means is that any data stored in <code>/data/db</code> on the MongoDB container will be stored under <code>data/mongo</code> (relative to the project root folder) on your laptop. Keep in mind that <code>/data/db</code> is where the MongoDB container stores data by default.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">"3.9"</span>
<span class="hljs-attr">services:</span>
  <span class="hljs-attr">mongo:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">"mongo:5.0.9"</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">.data/mongo:/data/db</span>
  <span class="hljs-attr">webapp:</span>
    <span class="hljs-attr">build:</span> <span class="hljs-string">.</span>
    <span class="hljs-attr">depends_on:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"mongo"</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"8080:8080"</span>
</code></pre>
<p>To start the application, we run <code>docker-compose up --build -d</code> which effectively does three things:</p>
<ul>
<li><p>creates a container running the MongoDB image - using the default MongoDB port 27017 for connections</p>
</li>
<li><p>re-builds the image for our REST API and creates a container running the service - the container will have port 8080 exposed so that you can connect from your laptop to the API</p>
</li>
<li><p>creates a virtual network connecting the two containers</p>
</li>
</ul>
<p>To stop the application and clean up the resources (containers and network), run <code>docker-compose down</code>.</p>
<p>The full commit for this step can be found <a target="_blank" href="https://github.com/nikist97/TaskManagementService/commit/587c6fb71c6ddb30f066bfd91aa345a9c7dd6a3e">here</a>.</p>
<h3 id="heading-repository-interface-implementation">Repository Interface Implementation</h3>
<p>Now that we have a MongoDB instance running, we need to create a new class - <strong>MongoDBTaskManagementRepository</strong> - that will implement the repository interface.</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MongoDBTaskManagementRepository</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">TaskManagementRepository</span> </span>{

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> MongoCollection&lt;MongoDBTask&gt; tasksCollection;

    <span class="hljs-meta">@Inject</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">MongoDBTaskManagementRepository</span><span class="hljs-params">(MongoCollection&lt;MongoDBTask&gt; tasksCollection)</span> </span>{
        <span class="hljs-keyword">this</span>.tasksCollection = tasksCollection;
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">save</span><span class="hljs-params">(Task task)</span> </span>{
        MongoDBTask mongoDBTask = toMongoDBTask(task);
        ReplaceOptions replaceOptions = <span class="hljs-keyword">new</span> ReplaceOptions()
                .upsert(<span class="hljs-keyword">true</span>);
        tasksCollection.replaceOne(eq(<span class="hljs-string">"_id"</span>, task.getIdentifier()), mongoDBTask, replaceOptions);
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> List&lt;Task&gt; <span class="hljs-title">getAll</span><span class="hljs-params">()</span> </span>{
        FindIterable&lt;MongoDBTask&gt; mongoDBTasks = tasksCollection.find();

        List&lt;Task&gt; tasks = <span class="hljs-keyword">new</span> ArrayList&lt;&gt;();
        <span class="hljs-keyword">for</span> (MongoDBTask mongoDBTask: mongoDBTasks) {
            tasks.add(fromMongoDBTask(mongoDBTask));
        }

        <span class="hljs-keyword">return</span> tasks;
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Optional&lt;Task&gt; <span class="hljs-title">get</span><span class="hljs-params">(String taskID)</span> </span>{
        Optional&lt;MongoDBTask&gt; mongoDBTask = Optional.ofNullable(
                tasksCollection.find(eq(<span class="hljs-string">"_id"</span>, taskID)).first());

        <span class="hljs-keyword">return</span> mongoDBTask.map(<span class="hljs-keyword">this</span>::fromMongoDBTask);
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">delete</span><span class="hljs-params">(String taskID)</span> </span>{
        Document taskIDFilter = <span class="hljs-keyword">new</span> Document(<span class="hljs-string">"_id"</span>, taskID);
        tasksCollection.deleteOne(taskIDFilter);
    }

    <span class="hljs-function"><span class="hljs-keyword">private</span> Task <span class="hljs-title">fromMongoDBTask</span><span class="hljs-params">(MongoDBTask mongoDBTask)</span> </span>{
        <span class="hljs-keyword">return</span> Task.builder(mongoDBTask.getTitle(), mongoDBTask.getDescription())
                .withIdentifier(mongoDBTask.getIdentifier())
                .withCompleted(mongoDBTask.isCompleted())
                .withCreatedAt(Instant.ofEpochMilli(mongoDBTask.getCreatedAt()))
                .build();
    }

    <span class="hljs-function"><span class="hljs-keyword">private</span> MongoDBTask <span class="hljs-title">toMongoDBTask</span><span class="hljs-params">(Task task)</span> </span>{
        MongoDBTask mongoDBTask = <span class="hljs-keyword">new</span> MongoDBTask();
        mongoDBTask.setIdentifier(task.getIdentifier());
        mongoDBTask.setTitle(task.getTitle());
        mongoDBTask.setDescription(task.getDescription());
        mongoDBTask.setCreatedAt(task.getCreatedAt().toEpochMilli());
        mongoDBTask.setCompleted(task.isCompleted());

        <span class="hljs-keyword">return</span> mongoDBTask;
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MongoDBTask</span> </span>{
        <span class="hljs-meta">@BsonProperty("_id")</span>
        <span class="hljs-keyword">private</span> String identifier;

        <span class="hljs-keyword">private</span> String title;

        <span class="hljs-keyword">private</span> String description;

        <span class="hljs-meta">@BsonProperty("created_at")</span>
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">long</span> createdAt;

        <span class="hljs-keyword">private</span> <span class="hljs-keyword">boolean</span> completed;
        ...
    }
...
}
</code></pre>
<p>Given that this is not a MongoDB-specific tutorial, I skipped a few implementation details around the Mongo DB Java driver, but in essence, we are using a very basic implementation that maps the internal <code>MongoDBTask</code> POJO to a Mongo DB document. For more details, see the official <a target="_blank" href="https://www.mongodb.com/developer/languages/java/java-mapping-pojos/">tutorial</a>.</p>
<p>The full commit for this step can be found <a target="_blank" href="https://github.com/nikist97/TaskManagementService/commit/b79ccab2341c4463c18a2905d5b58a87c13e989f">here</a>.</p>
<h3 id="heading-binding-everything-together">Binding Everything Together</h3>
<p>You might have noticed that the MongoDB repository implementation relies on a <code>MongoCollection&lt;MongoDBTask&gt;</code> instance to be injected. This class is an abstraction for interacting with an actual MongoDB collection. In this section, we will write the code for connecting to the database and binding an instance of this class. Since we are using Guice for dependency injection, we will encapsulate this logic into its own Guice module and then use the new module in the main application module.</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MongoDBModule</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractModule</span> </span>{

    <span class="hljs-meta">@Provides</span>
    <span class="hljs-keyword">private</span> MongoCollection&lt;MongoDBTaskManagementRepository.MongoDBTask&gt; provideMongoCollection() {
        ConnectionString connectionString = <span class="hljs-keyword">new</span> ConnectionString(System.getenv(<span class="hljs-string">"MongoDB_URI"</span>));

        CodecRegistry pojoCodecRegistry = fromProviders(PojoCodecProvider.builder().automatic(<span class="hljs-keyword">true</span>).build());
        CodecRegistry codecRegistry = fromRegistries(MongoClientSettings.getDefaultCodecRegistry(), pojoCodecRegistry);

        MongoClientSettings mongoClientSettings = MongoClientSettings.builder()
                .applyConnectionString(connectionString)
                .codecRegistry(codecRegistry)
                .build();

        MongoClient mongoClient = MongoClients.create(mongoClientSettings);
        MongoDatabase mongoDatabase = mongoClient.getDatabase(<span class="hljs-string">"tasks_management_db"</span>);
        <span class="hljs-keyword">return</span> mongoDatabase.getCollection(<span class="hljs-string">"tasks"</span>, MongoDBTaskManagementRepository.MongoDBTask.class);
    }
}
</code></pre>
<p>To use this new module, we need to install it in our main application module and change the binding to use the MongoDB repository implementation:</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ApplicationModule</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractModule</span> </span>{

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">configure</span><span class="hljs-params">()</span> </span>{
        bind(TaskManagementRepository.class).to(MongoDBTaskManagementRepository.class).in(Singleton.class);

        install(<span class="hljs-keyword">new</span> MongoDBModule());
    }

}
</code></pre>
<p>You can see in the MongoDB module that we are using a database with name <strong>tasks_management_db</strong> and a collection with name <strong>tasks</strong> but we never actually created these. The reason is that MongoDB will automatically create both the database and the collection as soon as we insert the first document (when we create the first task through our API).</p>
<p>The final piece for binding everything together is to configure the <strong>MongoDB_URI</strong> environment variable used for connecting to the database in the task management service container. This can be done through the Docker Compose file:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">"3.9"</span>
<span class="hljs-attr">services:</span>
  <span class="hljs-attr">webapp:</span>
    <span class="hljs-string">...</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">MongoDB_URI=mongodb://taskmanagementservice_mongo_1:27017</span>
    <span class="hljs-string">...</span>
</code></pre>
<p>The URI is built based on the default port the MongoDB container uses and the default hostname convention Docker Compose uses when building containers.</p>
<p>The full commit for this step can be found <a target="_blank" href="https://github.com/nikist97/TaskManagementService/commit/9e963b130976c29ef4f1b537e891d2a451d67cba">here</a>.</p>
<p>EDIT: After initially writing this blog post, docker-compose changed the default container naming convention which resulted in the MongoDB container being named <code>taskmanagementservice-mongo-1</code> instead of <code>taskmanagementservice_mongo_1</code>. That's why an additional code change was added to ensure the container names are fixed and the deployment still works:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">"3.9"</span>
<span class="hljs-attr">services:</span>
  <span class="hljs-attr">mongo:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">"mongo:5.0.9"</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">taskmanagementservice_mongodb</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">.data/mongo:/data/db</span>
  <span class="hljs-attr">webapp:</span>
    <span class="hljs-attr">build:</span> <span class="hljs-string">.</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">taskmanagementservice_api</span>
    <span class="hljs-attr">depends_on:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"mongo"</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">MongoDB_URI=mongodb://taskmanagementservice_mongodb:27017</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"8080:8080"</span>
</code></pre>
<p>The full commit for this change can be found <a target="_blank" href="https://github.com/nikist97/TaskManagementService/commit/dfc9a54131eb55991423fa312baac603bad1be22">here</a>.</p>
<h2 id="heading-testing-the-service">Testing The Service</h2>
<p>Before we start testing, let's re-build the full stack from scratch:</p>
<pre><code class="lang-bash">docker-compose down
docker-compose up --build -d
</code></pre>
<p>Now, both the service and MongoDB should be up and running. We will once again use curl to test the CRUD API (ideally these manual tests should be automated as integration tests but we will leave that for another article where we focus on testing):</p>
<ul>
<li>creating a few tasks</li>
</ul>
<pre><code class="lang-bash">curl -i -X POST -H <span class="hljs-string">"Content-Type:application/json"</span> -d <span class="hljs-string">"{\"title\": \"test-title\", \"description\":\"description\"}"</span> <span class="hljs-string">"http://localhost:8080/api/tasks"</span> 

HTTP/1.1 201 
Location: http://localhost:8080/api/tasks/cb06d6a1-960b-47eb-b44b-de0b01303020
Content-Length: 0
Date: Fri, 29 Jul 2022 09:20:25 GMT

curl -i -X POST -H <span class="hljs-string">"Content-Type:application/json"</span> -d <span class="hljs-string">"{\"title\": \"test-title\", \"description\":\"description\"}"</span> <span class="hljs-string">"http://localhost:8080/api/tasks"</span> 

HTTP/1.1 201 
Location: http://localhost:8080/api/tasks/3c4c7a7d-b680-4be5-9dd4-51d02225a700
Content-Length: 0
Date: Fri, 29 Jul 2022 09:20:32 GMT
</code></pre>
<ul>
<li>retrieving a task</li>
</ul>
<pre><code class="lang-bash">curl -i -X GET <span class="hljs-string">"http://localhost:8080/api/tasks/cb06d6a1-960b-47eb-b44b-de0b01303020"</span>

HTTP/1.1 200 
Content-Type: application/json
Content-Length: 159
Date: Fri, 29 Jul 2022 09:21:04 GMT

{<span class="hljs-string">"identifier"</span>:<span class="hljs-string">"cb06d6a1-960b-47eb-b44b-de0b01303020"</span>,<span class="hljs-string">"title"</span>:<span class="hljs-string">"test-title"</span>,<span class="hljs-string">"description"</span>:<span class="hljs-string">"description"</span>,<span class="hljs-string">"createdAt"</span>:<span class="hljs-string">"2022-07-29T09:20:25.410Z"</span>,<span class="hljs-string">"completed"</span>:<span class="hljs-literal">false</span>}
</code></pre>
<ul>
<li>retrieving a non-existing task</li>
</ul>
<pre><code class="lang-bash">curl -i -X GET <span class="hljs-string">"http://localhost:8080/api/tasks/random-task-id-123"</span>

HTTP/1.1 404 
Content-Type: application/json
Content-Length: 81
Date: Fri, 29 Jul 2022 09:21:19 GMT

{<span class="hljs-string">"message"</span>:<span class="hljs-string">"Task with the given identifier cannot be found - random-task-id-123"</span>}
</code></pre>
<ul>
<li>retrieving all tasks</li>
</ul>
<pre><code class="lang-bash">curl -i -X GET <span class="hljs-string">"http://localhost:8080/api/tasks"</span> 

HTTP/1.1 200 
Content-Type: application/json
Content-Length: 321
Date: Fri, 29 Jul 2022 09:21:30 GMT

[{<span class="hljs-string">"identifier"</span>:<span class="hljs-string">"cb06d6a1-960b-47eb-b44b-de0b01303020"</span>,<span class="hljs-string">"title"</span>:<span class="hljs-string">"test-title"</span>,<span class="hljs-string">"description"</span>:<span class="hljs-string">"description"</span>,<span class="hljs-string">"createdAt"</span>:<span class="hljs-string">"2022-07-29T09:20:25.410Z"</span>,<span class="hljs-string">"completed"</span>:<span class="hljs-literal">false</span>},{<span class="hljs-string">"identifier"</span>:<span class="hljs-string">"3c4c7a7d-b680-4be5-9dd4-51d02225a700"</span>,<span class="hljs-string">"title"</span>:<span class="hljs-string">"test-title"</span>,<span class="hljs-string">"description"</span>:<span class="hljs-string">"description"</span>,<span class="hljs-string">"createdAt"</span>:<span class="hljs-string">"2022-07-29T09:20:32.851Z"</span>,<span class="hljs-string">"completed"</span>:<span class="hljs-literal">false</span>}]
</code></pre>
<ul>
<li>deleting a task</li>
</ul>
<pre><code class="lang-bash">curl -i -X DELETE <span class="hljs-string">"http://localhost:8080/api/tasks/3c4c7a7d-b680-4be5-9dd4-51d02225a700"</span>                             

HTTP/1.1 204 
Date: Fri, 29 Jul 2022 09:22:11 GMT
</code></pre>
<ul>
<li>patching a task</li>
</ul>
<pre><code class="lang-bash">curl -i -X PATCH -H <span class="hljs-string">"Content-Type:application/json"</span> -d <span class="hljs-string">"{\"completed\": true, \"title\": \"new-title\", \"description\":\"new-description\"}"</span> <span class="hljs-string">"http://localhost:8080/api/tasks/cb06d6a1-960b-47eb-b44b-de0b01303020"</span>

HTTP/1.1 200 
Content-Length: 0
Date: Fri, 29 Jul 2022 09:22:34 GMT
</code></pre>
<p>So far, all the tests we executed look pretty much the same as the ones executed in the previous <a target="_blank" href="https://nsnotes.hashnode.dev/step-by-step-tutorial-for-building-a-rest-api-in-java#heading-testing-the-service">article</a>. However, we know that previously, restarting the service implied losing the data. Let's try this now by rebuilding the full stack again:</p>
<pre><code class="lang-bash">docker-compose down
docker-compose up --build -d
</code></pre>
<pre><code class="lang-bash">curl -i -X GET <span class="hljs-string">"http://localhost:8080/api/tasks"</span> 

HTTP/1.1 200 
Content-Type: application/json
Content-Length: 163
Date: Fri, 29 Jul 2022 09:27:25 GMT

[{<span class="hljs-string">"identifier"</span>:<span class="hljs-string">"cb06d6a1-960b-47eb-b44b-de0b01303020"</span>,<span class="hljs-string">"title"</span>:<span class="hljs-string">"new-title"</span>,<span class="hljs-string">"description"</span>:<span class="hljs-string">"new-description"</span>,<span class="hljs-string">"createdAt"</span>:<span class="hljs-string">"2022-07-29T09:20:25.410Z"</span>,<span class="hljs-string">"completed"</span>:<span class="hljs-literal">true</span>}]
</code></pre>
<p>As we can see, the data is now persistently stored - we still have the original task we initially created and then updated as part of testing.</p>
<h2 id="heading-summary">Summary</h2>
<p>In conclusion, what we've done as part of this tutorial is:</p>
<ul>
<li><p>setup a MongoDB container</p>
</li>
<li><p>use Docker Compose to manage a multi-container application</p>
</li>
<li><p>integrate a Java REST API with a MongoDB database</p>
</li>
</ul>
<p>The key point I was trying to show in this article was the fact that all we had to do to switch from an in-memory DB implementation to a proper DB technology was to create a new implementation of the repository interface, configure the connection to the DB inside a new guice module and then change one line in the already existing code so that our business logic will now use the new repository implementation.</p>
<p>We changed the guice binding from:</p>
<pre><code class="lang-java">bind(TaskManagementRepository.class).to(InMemoryTaskManagementRepository.class).in(Singleton.class);
</code></pre>
<p>to:</p>
<pre><code class="lang-java">bind(TaskManagementRepository.class).to(MongoDBTaskManagementRepository.class).in(Singleton.class);
</code></pre>
<p>We never touched any of the core business logic for our API and this is the beauty of clean architecture - we don't couple our code with the fact that we chose to persist our data in a database rather than in-memory. This is an implementation detail and changing our storage mechanism should not affect the rest of our code.</p>
]]></content:encoded></item><item><title><![CDATA[Step-By-Step Tutorial for Building a REST API in Java]]></title><description><![CDATA[Motivation
Having seen many tutorials on how to build REST APIs in Java using various combinations of frameworks and libraries, I decided to build my own API using the software suite that I have the most experience with. In particular, I wanted to us...]]></description><link>https://blog.teonibyte.com/step-by-step-tutorial-for-building-a-rest-api-in-java</link><guid isPermaLink="true">https://blog.teonibyte.com/step-by-step-tutorial-for-building-a-rest-api-in-java</guid><category><![CDATA[Java]]></category><category><![CDATA[maven]]></category><category><![CDATA[Docker]]></category><category><![CDATA[REST API]]></category><category><![CDATA[web application]]></category><dc:creator><![CDATA[Nikolay Stanchev]]></dc:creator><pubDate>Tue, 28 Jun 2022 17:27:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1670532263913/RhJXxos6U.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-motivation">Motivation</h2>
<p>Having seen many tutorials on how to build REST APIs in Java using various combinations of frameworks and libraries, I decided to build my own API using the software suite that I have the most experience with. In particular, I wanted to use:</p>
<ul>
<li><p><a target="_blank" href="https://maven.apache.org/">Maven</a> as the build and dependency management tool</p>
</li>
<li><p><a target="_blank" href="https://eclipse-ee4j.github.io/jersey/">Jersey</a> as the framework that provides an implementation of the <a target="_blank" href="https://jakarta.ee/specifications/restful-ws/">JAX-RS</a> specification</p>
</li>
<li><p><a target="_blank" href="https://tomcat.apache.org/">Tomcat</a> as the application server</p>
<ul>
<li>in particular, I wanted to run Tomcat in embedded mode so that I would end up with a simple executable jar file</li>
</ul>
</li>
<li><p><a target="_blank" href="https://github.com/google/guice">Guice</a> as the dependency injection framework</p>
</li>
</ul>
<p>The problem I faced was that I couldn't find any tutorials combining the software choices above, so I had to go through the process of combining the pieces myself. This didn't turn out to be a particularly straightforward task, which is why I decided to document the process on my blog and share it with others who might be facing similar problems.</p>
<h2 id="heading-project-summary">Project Summary</h2>
<p>For this tutorial, we are going to build the standard API for managing TODO items - i.e. a CRUD API that supports the functionalities of <strong>C</strong>reating, <strong>R</strong>etrieving, <strong>U</strong>pdating and <strong>D</strong>eleting tasks.</p>
<p>The API specification is given below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1655969913570/j5ms9vDkX.png" alt="Screenshot 2022-06-23 at 10.37.36.png" /></p>
<p>The full specification can be viewed in the <a class="post-section-overview" href="#heading-appendix">Appendix</a>.</p>
<p>To implement this API, we will use:</p>
<ul>
<li><p>Java 11 (OpenJDK)</p>
</li>
<li><p>Apache Maven v3.8.6</p>
</li>
<li><p>Ecplipse Jersey v2.35</p>
</li>
<li><p>Apache Tomcat v9.0.62</p>
</li>
<li><p>Guice v4.2.3</p>
</li>
</ul>
<p>For simplicity, I will avoid the use of any databases as part of this tutorial and instead use a pseudo-in-memory DB. However, we will see how easy it is to switch from an in-memory testing DB to an actual database when following a <a target="_blank" href="https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html">clean architecture</a>.</p>
<p>The goal is to end up with an executable jar file generated by Maven that will include the Tomcat application server and our API implementation. We will then dockerize the entire process of generating the file and executing it, and finally, run the service as a Docker container.</p>
<p>The following coding steps will only outline the most relevant pieces of code for this tutorial, but you can find the full code in the <a target="_blank" href="https://github.com/nikist97/TaskManagementService">GitHub repository</a>. For most steps, we will add unit tests that won't be referenced here but included in the code change itself. To run the tests at any given point in time, you can use <code>mvn clean test</code>.</p>
<h2 id="heading-coding-steps">Coding Steps</h2>
<h3 id="heading-step-1-project-setup">Step 1 - Project Setup</h3>
<p>As with every Maven project, we need a POM file (the file representing the <a target="_blank" href="https://maven.apache.org/pom.html#What_is_the_POM"><strong>P</strong>roject <strong>o</strong>bject <strong>M</strong>odel</a>). We start with a very basic POM which describes the project information and sets the JDK and JRE target versions to 11. This means that the project can use Java 11 language features (but no features from later versions) and will require a JRE version 11 or later to be executed. To avoid registering a domain name for this example project, I am using a group ID that corresponds to my GitHub username where this project will be hosted - <code>com.github.nikist97</code>.</p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">project</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://maven.apache.org/POM/4.0.0"</span> <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>
         <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">modelVersion</span>&gt;</span>4.0.0<span class="hljs-tag">&lt;/<span class="hljs-name">modelVersion</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- Project Information --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.github.nikist97<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>TaskManagementService<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>1.0-SNAPSHOT<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">packaging</span>&gt;</span>jar<span class="hljs-tag">&lt;/<span class="hljs-name">packaging</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">name</span>&gt;</span>TaskManagementService<span class="hljs-tag">&lt;/<span class="hljs-name">name</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">properties</span>&gt;</span>
        <span class="hljs-comment">&lt;!-- Maven-related properties used during the build process --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">project.build.sourceEncoding</span>&gt;</span>UTF-8<span class="hljs-tag">&lt;/<span class="hljs-name">project.build.sourceEncoding</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">maven.compiler.source</span>&gt;</span>11<span class="hljs-tag">&lt;/<span class="hljs-name">maven.compiler.source</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">maven.compiler.target</span>&gt;</span>11<span class="hljs-tag">&lt;/<span class="hljs-name">maven.compiler.target</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">properties</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">dependencies</span>&gt;</span>
        <span class="hljs-comment">&lt;!--     This is where we will declare libraries our project depends on     --&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependencies</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">build</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">plugins</span>&gt;</span>
            <span class="hljs-comment">&lt;!--     This is where we will declare plugins our project needs for the build process     --&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">plugins</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">build</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">project</span>&gt;</span>
</code></pre>
<p>The full commit for this step can be found <a target="_blank" href="https://github.com/nikist97/TaskManagementService/commit/36c4d1136d69cd0d2cb0ecee16b504d2f80b43d5">here</a>.</p>
<h3 id="heading-step-2-implementing-the-business-logic">Step 2 - Implementing the Business Logic</h3>
<p>We start with the most critical piece of software in general, which is our business logic. Ideally, this layer should be agnostic to the notion of any DB technologies or API protocols. Whether we implement an HTTP API using MongoDB on the backend or we use PostgreSQL and implement a command-line tool for interacting with our code, it should not affect the code for our business logic. In other words, the business logic should <strong>not depend</strong> on the persistence layer (the code interacting with the database) and the API layer (the code that will define the HTTP API endpoints).</p>
<p>The first thing to implement is our main entity class - <strong>Task</strong>. This class follows the builder pattern and provides argument validation. The required attributes are the task's <strong>title</strong> and <strong>description</strong>. For the rest of the attributes we can default to sensible values when not explicitly provided:</p>
<ul>
<li><p><strong>identifier</strong> is set to a random UUID</p>
</li>
<li><p><strong>createdAt</strong> is set to the current datetime</p>
</li>
<li><p><strong>completed</strong> is set to False</p>
</li>
</ul>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Task</span> </span>{

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String identifier;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String title;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String description;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> Instant createdAt;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">boolean</span> completed;

    ...

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TaskBuilder</span> </span>{

        ...

        <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-title">TaskBuilder</span><span class="hljs-params">(String title, String description)</span> </span>{
            validateArgNotNullOrBlank(title, <span class="hljs-string">"title"</span>);
            validateArgNotNullOrBlank(description, <span class="hljs-string">"description"</span>);

            <span class="hljs-keyword">this</span>.title = title;
            <span class="hljs-keyword">this</span>.description = description;
            <span class="hljs-keyword">this</span>.identifier = UUID.randomUUID().toString();
            <span class="hljs-keyword">this</span>.createdAt = Instant.now();
            <span class="hljs-keyword">this</span>.completed = <span class="hljs-keyword">false</span>;
        }

        ...

    }
}
</code></pre>
<p>Then, we define the interface we need for interacting with a persistence layer (i.e. a database or another storage mechanism). Notice that this interface belongs to the business layer because, ultimately, it is the business logic that decides what storage functionality we will need. The actual implementation of this interface, though (a MongoDB implementation or an in-memory DB or something else) will belong to the persistence layer, which we will implement in a subsequent step.</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">TaskManagementRepository</span> </span>{

    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">save</span><span class="hljs-params">(Task task)</span></span>;

    <span class="hljs-function">List&lt;Task&gt; <span class="hljs-title">getAll</span><span class="hljs-params">()</span></span>;

    <span class="hljs-function">Optional&lt;Task&gt; <span class="hljs-title">get</span><span class="hljs-params">(String taskID)</span></span>;

    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">delete</span><span class="hljs-params">(String taskID)</span></span>;
}
</code></pre>
<p>Finally, we implement the service class, which has the CRUD logic. The critical piece here is that this class doesn't rely on a concrete implementation of the repository interface - it is agnostic to what DB technology we decide to use later.</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TaskManagementService</span> </span>{

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> TaskManagementRepository repository;

    ...

    <span class="hljs-function"><span class="hljs-keyword">public</span> Task <span class="hljs-title">create</span><span class="hljs-params">(String title, String description)</span>  </span>{
        Task task = Task.builder(title, description).build();

        repository.save(task);

        <span class="hljs-keyword">return</span> task;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> Task <span class="hljs-title">update</span><span class="hljs-params">(String taskID, TaskUpdateRequest taskUpdateRequest)</span> </span>{
        Task oldTask = retrieve(taskID);

        Task newTask = oldTask.update(taskUpdateRequest);
        repository.save(newTask);

        <span class="hljs-keyword">return</span> newTask;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> List&lt;Task&gt; <span class="hljs-title">retrieveAll</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> repository.getAll();
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> Task <span class="hljs-title">retrieve</span><span class="hljs-params">(String taskID)</span> </span>{
        <span class="hljs-keyword">return</span> repository.get(taskID).orElseThrow(() -&gt;
                <span class="hljs-keyword">new</span> TaskNotFoundException(<span class="hljs-string">"Task with the given identifier cannot be found - "</span> + taskID));
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">delete</span><span class="hljs-params">(String taskID)</span> </span>{
        repository.delete(taskID);
    }
}
</code></pre>
<p>The way this code was written allows us to easily unit test our business logic in isolation by mocking the behaviuor of the repository interface. To achieve this, we will need to add two dependencies in the POM file:</p>
<pre><code class="lang-xml">        ...
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>junit<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>junit<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>4.11<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>test<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.mockito<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>mockito-core<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>3.5.13<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>test<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        ...
</code></pre>
<p>The full commit for this step can be found <a target="_blank" href="https://github.com/nikist97/TaskManagementService/commit/830233cb0b6cae95f66156e1794dee1c7cbdec7e">here</a>.</p>
<h3 id="heading-step-3-creating-stub-api-endpoints">Step 3 - Creating Stub API Endpoints</h3>
<p>The next step is to implement the API layer. For this project, we are implementing an HTTP REST API using Jersey. Therefore, we start by adding the dependency in the POM file.</p>
<pre><code class="lang-xml">        ...
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.glassfish.jersey.containers<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>jersey-container-servlet<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.35<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.glassfish.jersey.inject<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>jersey-hk2<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.35<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        ...
</code></pre>
<p>The second dependency is needed after Jersey 2.26 - https://eclipse-ee4j.github.io/jersey.github.io/release-notes/2.26.html - following this version users need to explicitly declare the dependency injection framework for Jersey to use - in this case, we go with HK2 which is what was used in previous releases.</p>
<p>Then we implement the resource class, which at this point only has stub methods that all return a status code 200 HTTP response with no response body.</p>
<pre><code class="lang-java"><span class="hljs-meta">@Path("/tasks")</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TaskManagementResource</span> </span>{

    <span class="hljs-meta">@POST</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Response <span class="hljs-title">createTask</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> Response.ok().build();
    }

    <span class="hljs-meta">@GET</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Response <span class="hljs-title">getTasks</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> Response.ok().build();
    }

    <span class="hljs-meta">@PATCH</span>
    <span class="hljs-meta">@Path("/{taskID}")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Response <span class="hljs-title">updateTask</span><span class="hljs-params">(<span class="hljs-meta">@PathParam("taskID")</span> String taskID)</span> </span>{
        <span class="hljs-keyword">return</span> Response.ok().build();
    }

    <span class="hljs-meta">@GET</span>
    <span class="hljs-meta">@Path("/{taskID}")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Response <span class="hljs-title">getTask</span><span class="hljs-params">(<span class="hljs-meta">@PathParam("taskID")</span> String taskID)</span> </span>{
        <span class="hljs-keyword">return</span> Response.ok().build();
    }

    <span class="hljs-meta">@DELETE</span>
    <span class="hljs-meta">@Path("/{taskID}")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Response <span class="hljs-title">deleteTask</span><span class="hljs-params">(<span class="hljs-meta">@PathParam("taskID")</span> String taskID)</span> </span>{

        <span class="hljs-keyword">return</span> Response.ok().build();
    }
}
</code></pre>
<p>We will also need an application config class to define the base URI for our API and to inform the framework about the task management resource class:</p>
<pre><code class="lang-java"><span class="hljs-meta">@ApplicationPath("/api")</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ApplicationConfig</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ResourceConfig</span> </span>{

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">ApplicationConfig</span><span class="hljs-params">()</span> </span>{
        register(TaskManagementResource.class);
    }

}
</code></pre>
<p>The full commit for this step can be found <a target="_blank" href="https://github.com/nikist97/TaskManagementService/commit/993b443e9821af374250d0eff34ccb9be81d0307">here</a>.</p>
<h3 id="heading-step-4-implementing-the-api-layer">Step 4 - Implementing the API Layer</h3>
<p>For this project, we will use JSON as the serialization data format for HTTP requests and responses.</p>
<p>To produce and consume JSON in our API, we need to add a library that's going to be responsible for the JSON serialization and deserialization of POJOs. We are going to use Jackson. The library we need to integrate Jersy with Jackson is given below:</p>
<pre><code class="lang-xml">        ...
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.glassfish.jersey.media<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>jersey-media-json-jackson<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.35<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        ...
</code></pre>
<p>Then we need to customize the behaviour of the JSON object mapper that will be used for serializing and deserializing the request and response POJOs. In this case, we disable ALLOW_COERCION_OF_SCALARS - this means that the service won't attempt to parse strings into numbers or booleans (e.g. <code>{"boolean_field":"true"}</code> will be rejected)</p>
<pre><code class="lang-java"><span class="hljs-meta">@Provider</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">JsonObjectMapperProvider</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ContextResolver</span>&lt;<span class="hljs-title">ObjectMapper</span>&gt; </span>{

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> ObjectMapper jsonObjectMapper;

    <span class="hljs-comment">/**
     * Create a custom JSON object mapper provider.
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">JsonObjectMapperProvider</span><span class="hljs-params">()</span> </span>{
        jsonObjectMapper = <span class="hljs-keyword">new</span> ObjectMapper();
        jsonObjectMapper.disable(ALLOW_COERCION_OF_SCALARS);
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> ObjectMapper <span class="hljs-title">getContext</span><span class="hljs-params">(Class&lt;?&gt; type)</span> </span>{
        <span class="hljs-keyword">return</span> jsonObjectMapper;
    }
}
</code></pre>
<p>Once again, we need to make Jersey aware of this provider class:</p>
<pre><code class="lang-java"><span class="hljs-meta">@ApplicationPath("/api")</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ApplicationConfig</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ResourceConfig</span> </span>{

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">ApplicationConfig</span><span class="hljs-params">()</span> </span>{
        register(TaskManagementResource.class);
        register(JsonObjectMapperProvider.class);
    }

}
</code></pre>
<p>Then we define the request and response POJOs. I will skip the code for these classes, but in summary, we need:</p>
<ul>
<li><p><strong>TaskCreateRequest</strong> - represents the JSON request body sent to the service when creating a new task</p>
</li>
<li><p><strong>TaskUpdateRequest</strong> - represents the JSON request body sent to the service when updating an existing task</p>
</li>
<li><p><strong>TaskResponse</strong> - represents the JSON response body sent to the client when retrieving task(s)</p>
</li>
</ul>
<p>The last part of this step is to replace the stub logic in the resource class with the actual API implementation that relies on the business logic encapsulated in the service class from <a class="post-section-overview" href="#heading-step-2-implementing-the-business-logic">step 2</a>.</p>
<pre><code class="lang-java"><span class="hljs-meta">@Path("/tasks")</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TaskManagementResource</span> </span>{

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> TaskManagementService service;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">TaskManagementResource</span><span class="hljs-params">(TaskManagementService service)</span> </span>{
        <span class="hljs-keyword">this</span>.service = service;
    }

    <span class="hljs-meta">@POST</span>
    <span class="hljs-meta">@Consumes(MediaType.APPLICATION_JSON)</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Response <span class="hljs-title">createTask</span><span class="hljs-params">(TaskCreateRequest taskCreateRequest)</span> </span>{
        validateArgNotNull(taskCreateRequest, <span class="hljs-string">"task-create-request-body"</span>);

        Task task = service.create(taskCreateRequest.getTitle(), taskCreateRequest.getDescription());

        String taskID = task.getIdentifier();

        URI taskRelativeURI = URI.create(<span class="hljs-string">"tasks/"</span> + taskID);
        <span class="hljs-keyword">return</span> Response.created(taskRelativeURI).build();
    }

    <span class="hljs-meta">@GET</span>
    <span class="hljs-meta">@Produces(MediaType.APPLICATION_JSON)</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> List&lt;TaskResponse&gt; <span class="hljs-title">getTasks</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> service.retrieveAll().stream()
                .map(TaskResponse::<span class="hljs-keyword">new</span>)
                .collect(Collectors.toUnmodifiableList());
    }

    <span class="hljs-meta">@PATCH</span>
    <span class="hljs-meta">@Path("/{taskID}")</span>
    <span class="hljs-meta">@Produces(MediaType.APPLICATION_JSON)</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Response <span class="hljs-title">updateTask</span><span class="hljs-params">(<span class="hljs-meta">@PathParam("taskID")</span> String taskID, TaskUpdateRequest taskUpdateRequest)</span> </span>{
        validateArgNotNull(taskUpdateRequest, <span class="hljs-string">"task-update-request-body"</span>);

        TaskUpdate update = <span class="hljs-keyword">new</span> TaskUpdate(taskUpdateRequest.getTitle(), taskUpdateRequest.getDescription(),
                taskUpdateRequest.isCompleted());

        service.update(taskID, update);

        <span class="hljs-keyword">return</span> Response.ok().build();
    }

    <span class="hljs-meta">@GET</span>
    <span class="hljs-meta">@Path("/{taskID}")</span>
    <span class="hljs-meta">@Produces(MediaType.APPLICATION_JSON)</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> TaskResponse <span class="hljs-title">getTask</span><span class="hljs-params">(<span class="hljs-meta">@PathParam("taskID")</span> String taskID)</span> </span>{
        Task task = service.retrieve(taskID);
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> TaskResponse(task);
    }

    <span class="hljs-meta">@DELETE</span>
    <span class="hljs-meta">@Path("/{taskID}")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Response <span class="hljs-title">deleteTask</span><span class="hljs-params">(<span class="hljs-meta">@PathParam("taskID")</span> String taskID)</span> </span>{
        service.delete(taskID);
        <span class="hljs-keyword">return</span> Response.noContent().build();
    }
</code></pre>
<p>The full commit for this step can be found <a target="_blank" href="https://github.com/nikist97/TaskManagementService/commit/cda2ff4ec39cdcba46272ed4eb35890bcf8933ff">here</a>.</p>
<h3 id="heading-step-5-implementing-the-storage-mechanism">Step 5 - Implementing the Storage Mechanism</h3>
<p>For simplicity, we are going to implement an in-memory storage implementation of the repository interface rather than relying on a specific DB technology. The implementation will store all tasks inside a map - the key is the task identifier and the value is the task itself. This is just enough for simple CRUD functionality.</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">InMemoryTaskManagementRepository</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">TaskManagementRepository</span> </span>{

    Map&lt;String, Task&gt; tasks = <span class="hljs-keyword">new</span> HashMap&lt;&gt;();

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">save</span><span class="hljs-params">(Task task)</span> </span>{
        tasks.put(task.getIdentifier(), task);
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> List&lt;Task&gt; <span class="hljs-title">getAll</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> tasks.values().stream()
                .collect(Collectors.toUnmodifiableList());
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Optional&lt;Task&gt; <span class="hljs-title">get</span><span class="hljs-params">(String taskID)</span> </span>{
        <span class="hljs-keyword">return</span> Optional.ofNullable(tasks.get(taskID));
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">delete</span><span class="hljs-params">(String taskID)</span> </span>{
        tasks.remove(taskID);
    }

}
</code></pre>
<p>The full commit for this step can be found <a target="_blank" href="https://github.com/nikist97/TaskManagementService/commit/d491f55f36e00f8fead99a2c1ed9a46685147e3d">here</a>.</p>
<h3 id="heading-step-6-binding-everything-together">Step 6 - Binding Everything Together</h3>
<p>Now that we have all the layers implemented, we need to bind them together with a dependency injection framework - in this case, we will use Guice to achieve that.</p>
<p>We start by adding Guice as a dependency in the POM file:</p>
<pre><code class="lang-xml">        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.google.inject<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>guice<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>4.2.3<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
</code></pre>
<p>Then we create a simple Guice module to bind the in-memory DB implementation to the repository interface. This means that for all classes that depend on the repository interface, Guice will inject the in-memory DB class. We use the <strong>Singleton</strong> scope because we want all classes that depend on the repository to re-use the same in-memory DB instance.</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ApplicationModule</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractModule</span> </span>{

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">configure</span><span class="hljs-params">()</span> </span>{
        bind(TaskManagementRepository.class).to(InMemoryTaskManagementRepository.class).in(Singleton.class);
    }

}
</code></pre>
<p>Note that if we decide to use an actual database, the code change is as simple as:</p>
<ul>
<li><p>implementing the wrapper class for the DB we choose - e.g. <strong>MongoDBTaskManagementRepository</strong></p>
</li>
<li><p>changing the binding above to point to the new implementation of the repository interface</p>
</li>
</ul>
<p>Now that we have the module implemented, we can add <strong>Inject</strong> annotation to all classes where the constructor has a dependency which needs to be injected by Guice. These would be the <strong>TaskManagementResource</strong> and the <strong>TaskManagementService</strong> classes. The magic of Guice (and dependency injection in general) is that the module above is enough to build the entire tree of dependencies in our code.</p>
<p><strong>TaskManagementResource</strong> depends on <strong>TaskManagementService</strong> which depends on <strong>TaskManagementRepository</strong>. Guice knows how to get an instance of the <strong>TaskManagementRepository</strong> interface so following this chain it also knows how to get an instance of the <strong>TaskManagementService</strong> and <strong>TaskManagementResource</strong> classes.</p>
<p>The final piece of work is to make Jersey aware of the Guice injector - remember Jersey uses HK2 as its dependency injection framework, so Jersey will rely on HK2 to be able to build a <strong>TaskManagementResource</strong> class. For HK2 to build a <strong>TaskManagementResource</strong> it needs to know about Guice's dependency injector container. To connect Guice and HK2, we are going to use something called the <a target="_blank" href="https://javaee.github.io/hk2/guice-bridge.html">Guice/HK2 Bridge</a>. It is a process of bridging the Guice container (the Injector class) into the HK2 container (the ServiceLocator class).</p>
<p>So we declare a dependency on the Guice/HK2 bridge library:</p>
<pre><code class="lang-xml">        ...
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.glassfish.hk2<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>guice-bridge<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.6.1<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        ...
</code></pre>
<p>Then we change the <strong>ApplicationConfig</strong> class to create the bridge between Guice and HK2. Notice that since the <strong>ApplicationConfig</strong> class is used by Jersey (and thus managed by HK2) we can easily inject the <strong>ServiceLocator</strong> instance (the HK2 container itself) into it.</p>
<pre><code class="lang-java">        <span class="hljs-meta">@Inject</span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">ApplicationConfig</span><span class="hljs-params">(ServiceLocator serviceLocator)</span> </span>{
            register(TaskManagementResource.class);
            register(JsonObjectMapperProvider.class);

            // <span class="hljs-function">bridge the Guice <span class="hljs-title">container</span> <span class="hljs-params">(Injector)</span> into the HK2 <span class="hljs-title">container</span> <span class="hljs-params">(ServiceLocator)</span>
            Injector injector </span>= Guice.createInjector(<span class="hljs-keyword">new</span> ApplicationModule());
            GuiceBridge.getGuiceBridge().initializeGuiceBridge(serviceLocator);
            GuiceIntoHK2Bridge guiceBridge = serviceLocator.getService(GuiceIntoHK2Bridge.class);
            guiceBridge.bridgeGuiceInjector(injector);
        }
</code></pre>
<p>The full commit for this step can be found <a target="_blank" href="https://github.com/nikist97/TaskManagementService/commit/fdb7e4bf8f6d15fd35e7cf8a746ac68141d59ed3">here</a>.</p>
<h3 id="heading-step-7-creating-the-application-launcher">Step 7 - Creating the Application Launcher</h3>
<p>The final critical step is configuring and starting the application server through a launcher class, which will serve as our main class for the executable jar file we are targeting.</p>
<p>We start with the code for starting an embedded Tomcat server. The dependency we need is:</p>
<pre><code class="lang-xml">    ...
    <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.tomcat.embed<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>tomcat-embed-core<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>9.0.62<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
    ...
</code></pre>
<p>Then we need a launcher class. This class is responsible for starting the embedded Tomcat server and registering a servlet container for the resource config we defined earlier (when we registered the resource class).</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Launcher</span> </span>{

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> Exception </span>{
        Tomcat tomcat = <span class="hljs-keyword">new</span> Tomcat();

        <span class="hljs-comment">// configure server port number</span>
        tomcat.setPort(<span class="hljs-number">8080</span>);

        <span class="hljs-comment">// remove defaulted JSP configs</span>
        tomcat.setAddDefaultWebXmlToWebapp(<span class="hljs-keyword">false</span>);

        <span class="hljs-comment">// add the web app</span>
        StandardContext ctx = (StandardContext) tomcat.addWebapp(<span class="hljs-string">"/"</span>, <span class="hljs-keyword">new</span> File(<span class="hljs-string">"."</span>).getAbsolutePath());
        ResourceConfig resourceConfig = <span class="hljs-keyword">new</span> ResourceConfig(ApplicationConfig.class);
        Tomcat.addServlet(ctx, "jersey-container-servlet", <span class="hljs-keyword">new</span> ServletContainer(resourceConfig));
        ctx.addServletMappingDecoded(<span class="hljs-string">"/*"</span>, <span class="hljs-string">"jersey-container-servlet"</span>);

        <span class="hljs-comment">// start the server</span>
        tomcat.start();
        System.out.println(<span class="hljs-string">"Server listening on "</span> + tomcat.getHost().getName() + <span class="hljs-string">":"</span> + tomcat.getConnector().getPort());
        tomcat.getServer().await();
    }
}
</code></pre>
<p>If using IntelliJ to code this project, then you should ideally be able to run the main method of the Launcher class. There is one caveat here - with the release of JDK 9 and after (and hence the introduction of the Java Platform Module System), reflective access is only allowed to publicly exported packages. This means that Guice will fail at runtime because it uses reflection to access JDK modules. See this StackOverflow <a target="_blank" href="https://stackoverflow.com/questions/41265266/how-to-solve-inaccessibleobjectexception-unable-to-make-member-accessible-m">post</a> for more information.</p>
<p>The only workaround I found so far for this was to add the following as a JVM option <code>--add-opens java.base/java.lang=ALL-UNNAMED</code> to the run configuration of the main method as suggested in the StackOverflow post I linked. This basically allows Guice to continue doing its reflection as in the pre-JDK 9 releases.</p>
<p>After we use the workaround above and test our launcher, we get to the part of generating an executable JAR file which can be used to start the service. To achieve this, we need the appassembler plugin. Note that we still need to add the <code>--add-opens java.base/java.lang=ALL-UNNAMED</code> JVM argument for the executable jar file to work.</p>
<pre><code class="lang-xml">         ...
         <span class="hljs-tag">&lt;<span class="hljs-name">plugins</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">plugin</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.codehaus.mojo<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>appassembler-maven-plugin<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.0.0<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">configuration</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">assembleDirectory</span>&gt;</span>target<span class="hljs-tag">&lt;/<span class="hljs-name">assembleDirectory</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">extraJvmArguments</span>&gt;</span>--add-opens java.base/java.lang=ALL-UNNAMED<span class="hljs-tag">&lt;/<span class="hljs-name">extraJvmArguments</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">programs</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">program</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">mainClass</span>&gt;</span>taskmanagement.Launcher<span class="hljs-tag">&lt;/<span class="hljs-name">mainClass</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">name</span>&gt;</span>taskmanagement_webapp<span class="hljs-tag">&lt;/<span class="hljs-name">name</span>&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">program</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">programs</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">configuration</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">executions</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">execution</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">phase</span>&gt;</span>package<span class="hljs-tag">&lt;/<span class="hljs-name">phase</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">goals</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">goal</span>&gt;</span>assemble<span class="hljs-tag">&lt;/<span class="hljs-name">goal</span>&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">goals</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">execution</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">executions</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">plugin</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">plugins</span>&gt;</span>
        ...
</code></pre>
<p>With this plugin, we can finally generate an executable file and then use it to start the service:</p>
<pre><code class="lang-bash">mvn clean package
./target/bin/taskmanagement_webapp
</code></pre>
<p>The full commit for this step can be found <a target="_blank" href="https://github.com/nikist97/TaskManagementService/commit/179c1ea722089eb62046fe18202908c1f152abc1">here</a>.</p>
<h3 id="heading-step-8-adding-exception-mappers">Step 8 - Adding Exception Mappers</h3>
<p>You might have noticed that so far we have defined two custom exceptions that are thrown when the service receives input data it cannot handle:</p>
<ul>
<li><p><strong>TaskNotFoundException</strong></p>
</li>
<li><p><strong>InvalidTaskDataException</strong></p>
</li>
</ul>
<p>If these exceptions aren't handled properly when encountered, then the embedded tomcat server will wrap them inside an internal server error (status code 500) which is not very user-friendly. As per the API specification we defined in the beginning (see <a class="post-section-overview" href="#heading-appendix">Appendix</a>), we want clients to receive a 404 status code if, for example, they use a task ID that doesn't exist.</p>
<p>To achieve this, we use exception mappers. When we register those mappers, Jersey will use them to transform instances of these exceptions to proper HTTP <strong>Response</strong> objects.</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TaskNotFoundExceptionMapper</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ExceptionMapper</span>&lt;<span class="hljs-title">TaskNotFoundException</span>&gt; </span>{

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Response <span class="hljs-title">toResponse</span><span class="hljs-params">(TaskNotFoundException exception)</span> </span>{
        <span class="hljs-keyword">return</span> Response
                .status(Response.Status.NOT_FOUND)
                .entity(<span class="hljs-keyword">new</span> ExceptionMessage(exception.getMessage()))
                .type(MediaType.APPLICATION_JSON)
                .build();
    }

}
</code></pre>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">InvalidTaskDataExceptionMapper</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ExceptionMapper</span>&lt;<span class="hljs-title">InvalidTaskDataException</span>&gt; </span>{

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Response <span class="hljs-title">toResponse</span><span class="hljs-params">(InvalidTaskDataException exception)</span> </span>{
        <span class="hljs-keyword">return</span> Response
                .status(Response.Status.BAD_REQUEST)
                .entity(<span class="hljs-keyword">new</span> ExceptionMessage(exception.getMessage()))
                .type(MediaType.APPLICATION_JSON)
                .build();
    }

}
</code></pre>
<pre><code class="lang-java">    <span class="hljs-meta">@Inject</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">ApplicationConfig</span><span class="hljs-params">(ServiceLocator serviceLocator)</span> </span>{
        ...
        register(InvalidTaskDataExceptionMapper.class);
        register(TaskNotFoundExceptionMapper.class);
        ...
    }
</code></pre>
<p>Notice the use of a new POJO - <strong>ExceptionMessage</strong> - which is used to convey the exception message as a JSON response. Now, whenever the business logic throws any of these exceptions, we will get a proper JSON response with the appropriate status code.</p>
<p>The full commit for this step can be found <a target="_blank" href="https://github.com/nikist97/TaskManagementService/commit/498169e38589013c8a9c6f5651f57a5df14e4c62">here</a>.</p>
<h2 id="heading-dockerizing-the-application">Dockerizing the Application</h2>
<p>There are lots of benefits to using <a target="_blank" href="https://www.docker.com/">Docker</a> but given that this article is not about containers, I won't spend time talking about them. I will only mention that I always prefer to run applications in a Docker container because it makes the build process much more efficient (think application portability, well-defined build behaviour, improved deployment process, etc.)</p>
<p>The Dockerfile for our service is relatively simple and based on the maven OpenJDK image. It automates what we did in <a class="post-section-overview" href="#heading-step-7-creating-the-application-launcher">step 7</a> - packaging the application and running the executable jar file.</p>
<pre><code class="lang-plaintext">FROM maven:3.8.5-openjdk-11-slim
WORKDIR /application

COPY . .

RUN mvn clean package

CMD ["./target/bin/taskmanagement_webapp"]
</code></pre>
<p>With this, we can build the container image and start our service as a Docker container. The commands below assume you have the Docker daemon running on your local machine.</p>
<pre><code class="lang-bash">docker build --tag task-management-service .
docker run -d -p 127.0.0.1:8080:8080 --name test-task-management-service task-management-service
</code></pre>
<p>Now the service should be running in the background and be accessible from your local machine on port 8080. For starting/stopping it, use this command:</p>
<pre><code class="lang-bash">docker start/stop test-task-management-service
</code></pre>
<h2 id="heading-testing-the-service">Testing the Service</h2>
<p>Now that we have the service running, we can use <a target="_blank" href="https://curl.se/">Curl</a> to send some test requests.</p>
<ul>
<li>creating a few tasks</li>
</ul>
<pre><code class="lang-bash">curl -i -X POST -H <span class="hljs-string">"Content-Type:application/json"</span> -d <span class="hljs-string">"{\"title\": \"test-title\", \"description\":\"description\"}"</span> <span class="hljs-string">"http://localhost:8080/api/tasks"</span> 

HTTP/1.1 201 
Location: http://localhost:8080/api/tasks/d2c4ed20-2538-44e5-bf19-150db9f6d83f
Content-Length: 0
Date: Tue, 28 Jun 2022 07:52:46 GMT

curl -i -X POST -H <span class="hljs-string">"Content-Type:application/json"</span> -d <span class="hljs-string">"{\"title\": \"test-title\", \"description\":\"description\"}"</span> <span class="hljs-string">"http://localhost:8080/api/tasks"</span>

HTTP/1.1 201 
Location: http://localhost:8080/api/tasks/64d85db4-905b-4c62-ba10-13fcb19a2546
Content-Length: 0
Date: Tue, 28 Jun 2022 07:52:47 GMT
</code></pre>
<ul>
<li>retrieving a task</li>
</ul>
<pre><code class="lang-bash">curl -i -X GET <span class="hljs-string">"http://localhost:8080/api/tasks/64d85db4-905b-4c62-ba10-13fcb19a2546"</span>

HTTP/1.1 200 
Content-Type: application/json
Content-Length: 162
Date: Tue, 28 Jun 2022 07:54:21 GMT

{<span class="hljs-string">"identifier"</span>:<span class="hljs-string">"64d85db4-905b-4c62-ba10-13fcb19a2546"</span>,<span class="hljs-string">"title"</span>:<span class="hljs-string">"test-title"</span>,<span class="hljs-string">"description"</span>:<span class="hljs-string">"description"</span>,<span class="hljs-string">"createdAt"</span>:<span class="hljs-string">"2022-06-28T07:52:47.872859Z"</span>,<span class="hljs-string">"completed"</span>:<span class="hljs-literal">false</span>}
</code></pre>
<ul>
<li>retrieving a non-existing task</li>
</ul>
<pre><code class="lang-bash">curl -i -X GET <span class="hljs-string">"http://localhost:8080/api/tasks/random-task-id-123"</span>                                                       

HTTP/1.1 404 
Content-Type: application/json
Content-Length: 81
Date: Tue, 28 Jun 2022 09:44:53 GMT

{<span class="hljs-string">"message"</span>:<span class="hljs-string">"Task with the given identifier cannot be found - random-task-id-123"</span>}
</code></pre>
<ul>
<li>retrieving all tasks</li>
</ul>
<pre><code class="lang-bash">curl -i -X GET <span class="hljs-string">"http://localhost:8080/api/tasks"</span>     

HTTP/1.1 200 
Content-Type: application/json
Content-Length: 490
Date: Tue, 28 Jun 2022 07:55:08 GMT

[{<span class="hljs-string">"identifier"</span>:<span class="hljs-string">"64d85db4-905b-4c62-ba10-13fcb19a2546"</span>,<span class="hljs-string">"title"</span>:<span class="hljs-string">"test-title"</span>,<span class="hljs-string">"description"</span>:<span class="hljs-string">"description"</span>,<span class="hljs-string">"createdAt"</span>:<span class="hljs-string">"2022-06-28T07:52:47.872859Z"</span>,<span class="hljs-string">"completed"</span>:<span class="hljs-literal">false</span>},{<span class="hljs-string">"identifier"</span>:<span class="hljs-string">"d2c4ed20-2538-44e5-bf19-150db9f6d83f"</span>,<span class="hljs-string">"title"</span>:<span class="hljs-string">"test-title"</span>,<span class="hljs-string">"description"</span>:<span class="hljs-string">"description"</span>,<span class="hljs-string">"createdAt"</span>:<span class="hljs-string">"2022-06-28T07:52:46.444179Z"</span>,<span class="hljs-string">"completed"</span>:<span class="hljs-literal">false</span>}]
</code></pre>
<ul>
<li>deleting a task</li>
</ul>
<pre><code class="lang-bash">curl -i -X DELETE <span class="hljs-string">"http://localhost:8080/api/tasks/64d85db4-905b-4c62-ba10-13fcb19a2546"</span>

HTTP/1.1 204 
Date: Tue, 28 Jun 2022 07:56:55 GMT
</code></pre>
<ul>
<li>patching a task</li>
</ul>
<pre><code class="lang-bash">curl -i -X PATCH -H <span class="hljs-string">"Content-Type:application/json"</span> -d <span class="hljs-string">"{\"completed\": true, \"title\": \"new-title\", \"description\":\"new-description\"}"</span> <span class="hljs-string">"http://localhost:8080/api/tasks/d2c4ed20-2538-44e5-bf19-150db9f6d83f"</span>

HTTP/1.1 200 
Content-Length: 0
Date: Tue, 28 Jun 2022 08:00:37 GMT

curl -i -X GET <span class="hljs-string">"http://localhost:8080/api/tasks/d2c4ed20-2538-44e5-bf19-150db9f6d83f"</span>   
HTTP/1.1 200 
Content-Type: application/json
Content-Length: 164
Date: Tue, 28 Jun 2022 08:01:07 GMT

{<span class="hljs-string">"identifier"</span>:<span class="hljs-string">"d2c4ed20-2538-44e5-bf19-150db9f6d83f"</span>,<span class="hljs-string">"title"</span>:<span class="hljs-string">"new-title"</span>,<span class="hljs-string">"description"</span>:<span class="hljs-string">"new-description"</span>,<span class="hljs-string">"createdAt"</span>:<span class="hljs-string">"2022-06-28T07:52:46.444179Z"</span>,<span class="hljs-string">"completed"</span>:<span class="hljs-literal">true</span>}
</code></pre>
<ul>
<li>patching a task with empty title</li>
</ul>
<pre><code class="lang-bash">curl -i -X PATCH -H <span class="hljs-string">"Content-Type:application/json"</span> -d <span class="hljs-string">"{\"title\": \"\"}"</span> <span class="hljs-string">"http://localhost:8080/api/tasks/d2c4ed20-2538-44e5-bf19-150db9f6d83f"</span>

HTTP/1.1 400 
Content-Type: application/json
Content-Length: 43
Date: Tue, 28 Jun 2022 09:47:09 GMT
Connection: close

{<span class="hljs-string">"message"</span>:<span class="hljs-string">"title cannot be null or blank"</span>}
</code></pre>
<h2 id="heading-future-improvements">Future Improvements</h2>
<p>What we have built so far is not a production-ready API, but it demonstrates how to get started with the software suite I mentioned at the beginning of this article when building a REST API. Here are some future improvements that can be made:</p>
<ul>
<li><p>using a database for persistent storage</p>
</li>
<li><p>adding user authentication and authorization - tasks should be scoped per user rather than being available globally</p>
</li>
<li><p>adding logging</p>
</li>
<li><p>adding KPI (Key Performance Indicators) metrics - things like the count of total requests, latency, failures count, etc.</p>
</li>
<li><p>adding a mapper for unexpected exceptions - we don't want to expose a stack trace if the service encounters an unexpected null pointer exception, instead we want a JSON response with status code 500</p>
</li>
<li><p>adding automated integration tests</p>
</li>
<li><p>adding a more verbose response to the patch endpoint - e.g. indicating whether the request resulted in a change or not</p>
</li>
<li><p>scanning packages and automatically registering provider and resource classes instead of manually registering them one-by-one</p>
</li>
<li><p>adding CORS (Cross-Origin-Resource-Sharing) support if we intend to call the API from a browser application hosted under a different domain</p>
</li>
<li><p>adding SSL support</p>
</li>
<li><p>adding rate limiting</p>
</li>
</ul>
<p>If you found this article helpful and would like to see a follow-up on the topics above, please comment or message me with a prefered choice of what you would like to learn about the most.</p>
<h2 id="heading-appendix">Appendix</h2>
<p>The full API specification using the Open API description format can be found below. You can use the <a target="_blank" href="https://editor.swagger.io/">Swagger Editor</a> to display the API specification in a more friendly manner.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">swagger:</span> <span class="hljs-string">'2.0'</span>

<span class="hljs-attr">info:</span>
  <span class="hljs-attr">description:</span> <span class="hljs-string">This</span> <span class="hljs-string">is</span> <span class="hljs-string">a</span> <span class="hljs-string">RESTful</span> <span class="hljs-string">task</span> <span class="hljs-string">management</span> <span class="hljs-string">API</span> <span class="hljs-string">specification.</span>
  <span class="hljs-attr">version:</span> <span class="hljs-number">1.0</span><span class="hljs-number">.0</span>
  <span class="hljs-attr">title:</span> <span class="hljs-string">Task</span> <span class="hljs-string">Management</span> <span class="hljs-string">API</span>
  <span class="hljs-attr">license:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">Apache</span> <span class="hljs-number">2.0</span>
    <span class="hljs-attr">url:</span> <span class="hljs-string">'http://www.apache.org/licenses/LICENSE-2.0.html'</span>

<span class="hljs-attr">host:</span> <span class="hljs-string">'localhost:8080'</span>
<span class="hljs-attr">basePath:</span> <span class="hljs-string">/api</span>

<span class="hljs-attr">schemes:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">http</span>

<span class="hljs-attr">paths:</span>

  <span class="hljs-string">/tasks:</span>
    <span class="hljs-attr">post:</span>
      <span class="hljs-attr">summary:</span> <span class="hljs-string">Create</span> <span class="hljs-string">a</span> <span class="hljs-string">new</span> <span class="hljs-string">task</span>
      <span class="hljs-attr">operationId:</span> <span class="hljs-string">createTask</span>
      <span class="hljs-attr">consumes:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">application/json</span>
      <span class="hljs-attr">parameters:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">in:</span> <span class="hljs-string">body</span>
          <span class="hljs-attr">name:</span> <span class="hljs-string">taskCreateRequest</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">new</span> <span class="hljs-string">task</span> <span class="hljs-string">object</span> <span class="hljs-string">that</span> <span class="hljs-string">needs</span> <span class="hljs-string">to</span> <span class="hljs-string">be</span> <span class="hljs-string">added</span> <span class="hljs-string">to</span> <span class="hljs-string">the</span> <span class="hljs-string">list</span> <span class="hljs-string">of</span> <span class="hljs-string">tasks</span>
          <span class="hljs-attr">required:</span> <span class="hljs-literal">true</span>
          <span class="hljs-attr">schema:</span>
            <span class="hljs-string">$ref:</span> <span class="hljs-string">'#/definitions/TaskCreateRequest'</span>
      <span class="hljs-attr">responses:</span>
        <span class="hljs-attr">'201':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">successfully</span> <span class="hljs-string">created</span> <span class="hljs-string">new</span> <span class="hljs-string">task</span>
        <span class="hljs-attr">'400':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">task</span> <span class="hljs-string">create</span> <span class="hljs-string">request</span> <span class="hljs-string">failed</span> <span class="hljs-string">validation</span>
    <span class="hljs-attr">get:</span>
      <span class="hljs-attr">summary:</span> <span class="hljs-string">Retrieve</span> <span class="hljs-string">all</span> <span class="hljs-string">existing</span> <span class="hljs-string">tasks</span>
      <span class="hljs-attr">operationId:</span> <span class="hljs-string">retrieveTasks</span>
      <span class="hljs-attr">produces:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">application/json</span>
      <span class="hljs-attr">responses:</span>
        <span class="hljs-attr">'200':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">successfully</span> <span class="hljs-string">retrieved</span> <span class="hljs-string">all</span> <span class="hljs-string">tasks</span>
          <span class="hljs-attr">schema:</span>
            <span class="hljs-attr">type:</span> <span class="hljs-string">array</span>
            <span class="hljs-attr">items:</span>
              <span class="hljs-string">$ref:</span> <span class="hljs-string">'#/definitions/TaskResponse'</span>

  <span class="hljs-string">'/tasks/{taskID}'</span><span class="hljs-string">:</span>
    <span class="hljs-attr">get:</span>
      <span class="hljs-attr">summary:</span> <span class="hljs-string">Retrieve</span> <span class="hljs-string">task</span>
      <span class="hljs-attr">operationId:</span> <span class="hljs-string">retrieveTask</span>
      <span class="hljs-attr">produces:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">application/json</span>
      <span class="hljs-attr">parameters:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">taskID</span>
          <span class="hljs-attr">in:</span> <span class="hljs-string">path</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">task</span> <span class="hljs-string">identifier</span>
          <span class="hljs-attr">required:</span> <span class="hljs-literal">true</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
      <span class="hljs-attr">responses:</span>
        <span class="hljs-attr">'200':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">successfully</span> <span class="hljs-string">retrieved</span> <span class="hljs-string">task</span>
          <span class="hljs-attr">schema:</span>
            <span class="hljs-string">$ref:</span> <span class="hljs-string">'#/definitions/TaskResponse'</span>
        <span class="hljs-attr">'404':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">task</span> <span class="hljs-string">not</span> <span class="hljs-string">found</span>
    <span class="hljs-attr">patch:</span>
      <span class="hljs-attr">summary:</span> <span class="hljs-string">Update</span> <span class="hljs-string">task</span>
      <span class="hljs-attr">operationId:</span> <span class="hljs-string">updateTask</span>
      <span class="hljs-attr">consumes:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">application/json</span>
      <span class="hljs-attr">parameters:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">taskID</span>
          <span class="hljs-attr">in:</span> <span class="hljs-string">path</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">task</span> <span class="hljs-string">identifier</span>
          <span class="hljs-attr">required:</span> <span class="hljs-literal">true</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">taskUpdateRequest</span>
          <span class="hljs-attr">in:</span> <span class="hljs-string">body</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">task</span> <span class="hljs-string">update</span> <span class="hljs-string">request</span>
          <span class="hljs-attr">required:</span> <span class="hljs-literal">true</span>
          <span class="hljs-attr">schema:</span>
            <span class="hljs-string">$ref:</span> <span class="hljs-string">'#/definitions/TaskUpdateRequest'</span>
      <span class="hljs-attr">responses:</span>
        <span class="hljs-attr">'200':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">successfully</span> <span class="hljs-string">updated</span> <span class="hljs-string">task</span>
        <span class="hljs-attr">'400':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">task</span> <span class="hljs-string">update</span> <span class="hljs-string">request</span> <span class="hljs-string">failed</span> <span class="hljs-string">validation</span>
        <span class="hljs-attr">'404':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">task</span> <span class="hljs-string">not</span> <span class="hljs-string">found</span>
    <span class="hljs-attr">delete:</span>
      <span class="hljs-attr">summary:</span> <span class="hljs-string">Delete</span> <span class="hljs-string">task</span>
      <span class="hljs-attr">operationId:</span> <span class="hljs-string">deleteTask</span>
      <span class="hljs-attr">parameters:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">taskID</span>
          <span class="hljs-attr">in:</span> <span class="hljs-string">path</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">task</span> <span class="hljs-string">identifier</span>
          <span class="hljs-attr">required:</span> <span class="hljs-literal">true</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
      <span class="hljs-attr">responses:</span>
        <span class="hljs-attr">'204':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">&gt;-
            successfully deleted task or task with the given identifier did not
            exist
</span>
<span class="hljs-attr">definitions:</span>
  <span class="hljs-attr">TaskCreateRequest:</span>
    <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
    <span class="hljs-attr">required:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">title</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">description</span>
    <span class="hljs-attr">properties:</span>
      <span class="hljs-attr">title:</span>
        <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
      <span class="hljs-attr">description:</span>
        <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
  <span class="hljs-attr">TaskUpdateRequest:</span>
    <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
    <span class="hljs-attr">properties:</span>
      <span class="hljs-attr">title:</span>
        <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
      <span class="hljs-attr">description:</span>
        <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
      <span class="hljs-attr">completed:</span>
        <span class="hljs-attr">type:</span> <span class="hljs-string">boolean</span>
  <span class="hljs-attr">TaskResponse:</span>
    <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
    <span class="hljs-attr">required:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">identifier</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">title</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">description</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">completed</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">createdAt</span>
    <span class="hljs-attr">properties:</span>
      <span class="hljs-attr">identifier:</span>
        <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
      <span class="hljs-attr">title:</span>
        <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
      <span class="hljs-attr">description:</span>
        <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
      <span class="hljs-attr">createdAt:</span>
        <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
        <span class="hljs-attr">format:</span> <span class="hljs-string">date-time</span>
      <span class="hljs-attr">completed:</span>
        <span class="hljs-attr">type:</span> <span class="hljs-string">boolean</span>
</code></pre>
]]></content:encoded></item><item><title><![CDATA[A Guide for Common SSH Use Cases]]></title><description><![CDATA[What is SSH
SSH is a network protocol allowing two machines (physical or virtual) to establish an encrypted connection and comminucate with each other over an unsecure network without compromising the confidentiality and integrity of the exchanged me...]]></description><link>https://blog.teonibyte.com/a-guide-for-common-ssh-use-cases</link><guid isPermaLink="true">https://blog.teonibyte.com/a-guide-for-common-ssh-use-cases</guid><category><![CDATA[ssh]]></category><category><![CDATA[server]]></category><category><![CDATA[networking]]></category><category><![CDATA[login]]></category><category><![CDATA[Devops]]></category><dc:creator><![CDATA[Nikolay Stanchev]]></dc:creator><pubDate>Mon, 13 Jun 2022 10:30:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1670532529514/l1EptLVFe.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-what-is-ssh">What is SSH</h3>
<p><a target="_blank" href="https://en.wikipedia.org/wiki/Secure_Shell">SSH</a> is a network protocol allowing two machines (physical or virtual) to establish an encrypted connection and comminucate with each other over an unsecure network without compromising the confidentiality and integrity of the exchanged messages. The protocol uses public-key (a.k.a. asymmetric) cryptography to authenticate an SSH client that wants to connect to an SSH server.</p>
<p><a target="_blank" href="https://www.openssh.com/">OpenSSH</a> is an implementation of the SSH protocol. All the usage examples given in the next sections are based on it.</p>
<h3 id="heading-ssh-basic-usage">SSH Basic Usage</h3>
<p>A very common usage of SSH is to login to a remote server. Assuming you have a private/public key pair on your local machine and the public key is also distributed on the server you are trying to login to, then connecting is as simple as running:</p>
<pre><code class="lang-bash">ssh &lt;server domain name&gt;
------------------------------
ssh myserver.com
</code></pre>
<p>This assumes two things:</p>
<ul>
<li>you have an SSH agent running with the appropriate private key added or the path to the private key follows a standard naming convention (e.g. <code>~/.ssh/id_rsa</code>) - you might need to run <code>ssh-add &lt;path to private key&gt;</code> if this is not the case<ul>
<li><code>~/.ssh/id_rsa</code> is one of the standard naming conventions that the SSH client automatically attempts to use when authenticating with a server</li>
</ul>
</li>
<li>you want to authenticate with the username on your local machine<ul>
<li>this assumption will not hold if on my laptop I use something like <strong>nikolay</strong> as the username but on a server running Ubuntu only the <strong>ubuntu</strong> user exists</li>
</ul>
</li>
</ul>
<p>The following command can be used to change the username to authenticate with:</p>
<pre><code class="lang-bash">ssh &lt;username&gt;@&lt;server domain name&gt;
---------------------------------------------
ssh ubuntu@myserver.com
</code></pre>
<h3 id="heading-advanced-ssh-usage-tunneling">Advanced SSH Usage - Tunneling</h3>
<p>A more advanced usage of SSH is the so called SSH tunneling (a.k.a. SSH port forwarding). SSH tunneling is a process of establishing communication between a client (in this case, your local machine) and a server (a remote machine) through a jump server (the machine running an SSH server). This is achieved by mapping ports between the client and the SSH server (hence the name "port forwarding").</p>
<p>This can be visualized with the following diagram:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1655104308513/mBLXSsNwb.png" alt="Screenshot 2022-06-13 at 10.10.58.png" class="image--center mx-auto" /></p>
<p>Keep in mind that the remote machine might be the jump server itself.</p>
<p>There are 3 types of port forwarding, the behavior of which will be illustrated in the following sections. All the example commands assume that the client's public key is already distributed to the SSH server and that the client can successfully login to the SSH server using the command given in the previous section.</p>
<p><strong>N.B.</strong> remember that if using a different username on your local machine and on the SSH server, then you need to include <code>&lt;SSH server username&gt;@</code> in front of all the "jump server" references for the examples below to work - for example, instead of <code>mysshserver.com</code> you might have to use something like <code>user@mysshserver.com</code></p>
<h4 id="heading-local-port-forwarding">Local Port Forwarding</h4>
<p>Local port forwarding is the process of tunneling connections initiated by the client machine through a jump server and forwarding these connections to a remote server or the jump server itself.</p>
<p>To initiate this, run the command below on your local machine:</p>
<pre><code class="lang-bash">ssh -N -f -L &lt;<span class="hljs-built_in">local</span> port&gt;:&lt;target server domain name&gt;:&lt;remote port&gt; &lt;jump server domain name&gt;
------------------------------------------------------------------------------------------------------
ssh -N -f -L 5001:mytargetapp.com:5002  mysshserver.com
</code></pre>
<p>With the example above, all connections to port 5001 on your local machine will be tunneled (in an encrypted manner) to the SSH jump server (<code>mysshserver.com</code>) and then forwarded to <code>mytargetapp.com</code> at port 5002.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1655110178669/5RlSrH_K4.png" alt="Screenshot 2022-06-13 at 11.35.09.png" class="image--center mx-auto" /></p>
<p>The CLI flags are explained below:</p>
<ul>
<li><strong>-L</strong> - this is the key flag that instructs the SSH client to execute local port forwarding </li>
<li><strong>-N</strong> - by default the SSH client will open a session even when executing local port forwarding; this flag instructs the client to not execute a remote command (i.e. only forward ports)</li>
<li><strong>-f</strong> -  this instructs the ssh client to run in the background (so that you don't need to keep your terminal open while port forwarding is running)</li>
</ul>
<p>Another example where we are not forwarding to a remote machine but to the jump server itself:</p>
<pre><code class="lang-bash">ssh -N -f -L 5001:localhost:5002  mysshserver.com
</code></pre>
<p>With this, any connections to port 5001 on your local machine are tunneled to port 5002 on the jump server itself (<code>mysshserver.com</code>).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1655110212011/PBk-1AGkF.png" alt="Screenshot 2022-06-13 at 11.47.18.png" class="image--center mx-auto" /></p>
<h4 id="heading-remote-port-forwarding">Remote Port Forwarding</h4>
<p>Remote port forwarding is the reverse process of local port forwarding - forwarding connections initiated by a remote machine (or the jump server itself) through the jump server and tunneling these connections back to your local machine on the specified target port.</p>
<p>To initiate this, run the command below on your local machine:</p>
<pre><code class="lang-bash">ssh -N -f -R &lt;remote port&gt;:localhost:&lt;<span class="hljs-built_in">local</span> port&gt; &lt;jump server domain name&gt;
------------------------------------------------------------------------------------------------------
ssh -N -f -R 5001:localhost:5002  mysshserver.com
</code></pre>
<p>The example above shows a scenario where port 5001 will be open for connections on the jump server (<code>mysshserver.com</code>), any connection to it will be tunneled back to your local machine (in an encrypted manner), and then forwarded to the application listening on the target port - 5002. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1655110279483/NP2EnlWqb.png" alt="Screenshot 2022-06-13 at 11.04.30.png" class="image--center mx-auto" /></p>
<p>The only new CLI flag in this case is <strong>-R</strong> which instructs the SSH client to execute remote port forwarding. Remote port forwarding is a very common solution if you want to temporarily expose an application running on your local machine to the public (assuming the SSH server is publicly available on the internet) or to someone that has network access to the SSH server in general (for example, through a VPN). </p>
<h4 id="heading-dynamic-port-forwarding">Dynamic Port Forwarding</h4>
<p>Dynamic port forwarding is similar to local port forwarding in the sense that local connections are tunneled (in an encrypted manner) to the SSH jump server. The difference is that with dynamic port forwarding you don't need to specify a target host or port - all connections are forwarded to the original target host and target port through the SSH server. In technical terms, dynamic port forwarding is the process of using the SSH server as a <a target="_blank" href="https://en.wikipedia.org/wiki/SOCKS">SOCKS5</a> proxy server. </p>
<p>To initiate this run:</p>
<pre><code class="lang-bash">ssh -N -f -D &lt;<span class="hljs-built_in">local</span> proxy port&gt; &lt;jump server domain name&gt;
------------------------------------------------------------------------------------------------------
ssh -N -f -D 1080 mysshserver.com
</code></pre>
<p>The new CLI flag in this case is <strong>-D</strong> which instructs the SSH client to execute dynamic port forwarding. The recommendation is to use port 1080 as it is the <a target="_blank" href="https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml">standard</a> SOCKS protocol port number.</p>
<p>Then you can configure your browser (say Firefox - see this <a target="_blank" href="https://support.mozilla.org/en-US/kb/connection-settings-firefox">tutorial</a>) or any other application that supports SOCKS/SOCKS5 proxies to use this tunnel and securely forward your network traffic through the SSH jump server (<code>mysshserver.com</code>). In essense, this gives you a very basic VPN functionality by hiding data about your local machine - websites you visit will see traffic that seems to be coming from the SSH server.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1655112378928/gpbMum3q3.png" alt="Screenshot 2022-06-13 at 12.21.48.png" /></p>
<p>The diagram above illustrates an example where the client is accessing <code>somewebsite.com</code> through dynamic port forwarding. The browser uses the proxy to connect to the website. The communciation between the local machine and the SSH server (<code>mysshserver.com</code>) is encrypted. The SSH server then forwards the request to the actual website. From the perspective of <code>somewebsite.com</code> the conenction was initiated from the SSH server rather than the local machine.</p>
]]></content:encoded></item><item><title><![CDATA[6 Things You Can Easily Do With Nginx]]></title><description><![CDATA[Introduction
Nginx is by far one of the most famous and most commonly deployed web servers out in the world - check the web servers' usage statistics provided by W3Techs. Nginx can also act as a proxy server, load balancer, caching server, etc. - fee...]]></description><link>https://blog.teonibyte.com/6-things-you-can-easily-do-with-nginx</link><guid isPermaLink="true">https://blog.teonibyte.com/6-things-you-can-easily-do-with-nginx</guid><category><![CDATA[Web Development]]></category><category><![CDATA[nginx]]></category><category><![CDATA[server]]></category><category><![CDATA[http]]></category><category><![CDATA[APIs]]></category><dc:creator><![CDATA[Nikolay Stanchev]]></dc:creator><pubDate>Fri, 10 Jun 2022 15:22:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1670532750131/Z6ReUm4n8.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-introduction">Introduction</h3>
<p><a target="_blank" href="https://www.nginx.com/">Nginx</a> is by far one of the most famous and most commonly deployed web servers out in the world - check the <a target="_blank" href="https://w3techs.com/technologies/overview/web_server">web servers' usage statistics</a> provided by W3Techs. Nginx can also act as a proxy server, load balancer, caching server, etc. - feel free to read the <a target="_blank" href="https://www.nginx.com/resources/glossary/nginx/">official docs</a> for more information on what the product can offer you.</p>
<p>My first experience with Nginx was when trying to use it as an API gateway (more like a single entry point for a system) proxying requests to multiple back-end services. I fell in love with the simplicity of a product with so many capabilities. Since then, I've used it for multiple projects and would still prefer it in comparison to alternative web servers.</p>
<p>I decided to write this post to illustrate some of the features I found super useful during my experience with Nginx which in my opinion turned out to be incredibly simple to configure. In no particular order, the list is given in the next section.</p>
<p>If you don't understand the http/server/location directives in the configuration examples below or you haven't seen any Nginx configuration before, please refer to this <a target="_blank" href="https://docs.nginx.com/nginx/admin-guide/web-server/web-server/">guide</a> for more details.</p>
<h3 id="heading-how-tos">How-tos</h3>
<h4 id="heading-limit-request-body-size">Limit request body size</h4>
<p>Very often when developing an API we let users upload arbitrary files - be they image files, video files, archives, etc. This would commonly imply sending a large amount of bytes as part of the HTTP request body. When left with no validation this functionality can lead to unexpected storage costs or even memory exhaustion if the backend server loads the whole file into memory. Nginx can easily provide protection for this by enforcing a limit on the size of the request body. Here is an example configuration.</p>
<pre><code class="lang-nginx"><span class="hljs-section">http</span> {
    ...
    <span class="hljs-attribute">client_max_body_size</span> <span class="hljs-number">500M</span>;
    ... 
}
</code></pre>
<p>Yes, it is that simple. This line is enough to ensure the body of all HTTP requests sent to this host does not exceed 500 megabytes. Any requests that exceed the limit will receive a client error response with status code 413 (Request Entity Too Large).</p>
<p>Another example is given below - enforcing the limit for a particular URI pattern rather than for every single request:</p>
<pre><code class="lang-nginx"><span class="hljs-section">http</span> {
    ...
    <span class="hljs-section">server</span> {
        ...
        <span class="hljs-attribute">listen</span> <span class="hljs-number">80</span>;

        <span class="hljs-attribute">location</span> /api/pictures {
            ...
            <span class="hljs-attribute">client_max_body_size</span> <span class="hljs-number">500M</span>;
            ...
        }
        ...
    }
    ...
}
</code></pre>
<p>The config above defines an HTTP server listening on port 80 and enforces all requests sent to URIs starting with <code>/api/pictures</code> to have a maximum body size of 500 megabytes. For more details on this, please see the <a target="_blank" href="https://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size">official docs</a></p>
<p><strong>N.B. the default threshold is 1 megabyte</strong></p>
<h4 id="heading-add-http-basic-authentication">Add HTTP basic authentication</h4>
<p>A common use case website developers have is restricting access to certain sub-pages of their website. Personally, what I also commonly do during manual testing of a new website/API is restrict access to the entire application to a particular set of test users until the release is official. Nginx's <a target="_blank" href="https://nginx.org/en/docs/http/ngx_http_auth_basic_module.html">auth basic module</a> can easily allow us to do just that. For example:</p>
<pre><code class="lang-nginx"><span class="hljs-section">http</span> {
    ...
    <span class="hljs-attribute">auth_basic</span> <span class="hljs-string">"Please authenticate"</span>;
    <span class="hljs-attribute">auth_basic_user_file</span> /etc/nginx/.htpasswd;
    ... 
}
</code></pre>
<p>This config restricts the entire Nginx server using <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication">HTTP Basic Authentication</a>. Keep in mind the <code>/etc/nginx/.htpasswd</code> file must contain the list of <code>&lt;username&gt;:&lt;password&gt;</code> pairs and is expected to follow a certain format. Please see this <a target="_blank" href="https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-http-basic-authentication/">tutorial</a> for how to generate the necessary password file.</p>
<p>The same configuration can be used to only restrict a particular URI pattern:</p>
<pre><code class="lang-nginx"><span class="hljs-section">http</span> {
    ...
    <span class="hljs-section">server</span> {
        ...
        <span class="hljs-attribute">listen</span> <span class="hljs-number">80</span>;

        <span class="hljs-attribute">location</span> /admin {
            ...
            <span class="hljs-attribute">auth_basic</span> <span class="hljs-string">"Please authenticate"</span>;
            <span class="hljs-attribute">auth_basic_user_file</span> /etc/nginx/.htpasswd;
            ...
        }
        ...
    }
    ...
}
</code></pre>
<h4 id="heading-limit-api-access-to-read-only-requests">Limit API access to read-only requests</h4>
<p>Sometimes when exposing a local back-end service to the public through Nginx (acting as a proxy) I only want to allow read-only (e.g. GET and HEAD) requests that the front-end needs for retrieving content - for example, I might not want to expose publicly any ops-related DELETE API endpoints. Nginx supports this through the <a target="_blank" href="https://nginx.org/en/docs/http/ngx_http_core_module.html#limit_except">limit_except</a> directive:</p>
<pre><code class="lang-nginx"><span class="hljs-section">http</span> {
    ...
    <span class="hljs-section">server</span> {
        ...
        <span class="hljs-attribute">listen</span> <span class="hljs-number">80</span>;

        <span class="hljs-attribute">location</span> /api {
            ...
            <span class="hljs-attribute">limit_except</span> GET {
                <span class="hljs-attribute">deny</span>  all;
            }
            ...
        }
        ...
    }
    ...
}
</code></pre>
<p><strong>N.B. the HEAD method is automatically included when allowing the GET method</strong></p>
<h4 id="heading-add-ssl-support">Add SSL support</h4>
<p>Who doesn't love the message from our browsers indicating that the connection with a particular website is secure? To enable this for an Nginx web server we can use the <a target="_blank" href="https://nginx.org/en/docs/http/ngx_http_ssl_module.html">Nginx SSL module</a>:</p>
<pre><code class="lang-nginx"><span class="hljs-section">http</span> {
    ...
    <span class="hljs-section">server</span> {
        ...
        <span class="hljs-attribute">listen</span> <span class="hljs-number">443</span> ssl;

        <span class="hljs-attribute">ssl_certificate</span> &lt;path to certificate file in PEM format&gt;;
        <span class="hljs-attribute">ssl_certificate_key</span> &lt;path to private/secret key file in PEM format&gt;;
        ...
    }
    ...
}
</code></pre>
<p>This config assumes you know how to generate an SSL certificate. One of the simplest ways to do this is to use <strong>Let's encrypt</strong> - follow this <a target="_blank" href="https://letsencrypt.org/getting-started/">tutorial</a>. Ultimately, you should end up with <strong>fullchain.pem</strong> and <strong>privkey.pem</strong>; these are the files you need for the <strong>ssl_certiciate</strong> and <strong>ssl_certificate_key</strong> directives respectively.</p>
<h4 id="heading-rate-limiting-clients">Rate limiting clients</h4>
<p>Rate limiting is the process of ensuring that an individual client cannot exceed a given threshold of requests for a given unit of time - e.g. ensuring that each client can send a maximum of 5 requests per second. The module for configuring this is the <a target="_blank" href="https://nginx.org/en/docs/http/ngx_http_limit_req_module.html">Nginx HTTP Limit Request Module</a>.</p>
<pre><code class="lang-nginx"><span class="hljs-section">http</span> {
    ...
    <span class="hljs-attribute">limit_req_zone</span> <span class="hljs-variable">$binary_remote_addr</span> zone=books:<span class="hljs-number">10m</span> rate=5r/s;
    ...
    <span class="hljs-section">server</span> {
        ...
        <span class="hljs-attribute">listen</span> <span class="hljs-number">80</span>;

        <span class="hljs-attribute">location</span> /api/books {
            ...
            <span class="hljs-attribute">limit_req</span> zone=books;
            ...
        }
        ...
    }
    ...
}
</code></pre>
<p>Here we need one directive (<strong>limit_req_zone</strong>) for defining the rate limiting "zone" (a re-usable rate limiting configuration) and another directive (<strong>limit_req</strong>) for indicating that we want to enable the rate limiting zone for a particular context (e.g. a URI location pattern). The <strong>limit_req</strong> directive can also be used inside a server or http context - in other words, rate limiting can be applied for all virtual servers, for an individual virtual server or for a particular URI location pattern.</p>
<p>By default, clients that exceed the rate limiting threshold will receive a status code 503 Service Unavailable. This is also configurable with the <strong>limit_req_status</strong> directive:</p>
<pre><code class="lang-plaintext">http {
    ...
    limit_req_zone $binary_remote_addr zone=books:10m rate=5r/s;
    ...
    server {
        ...
        listen 80;

        location /api/books {
            ...
            limit_req zone=books;
            limit_req_status 429;
            ...
        }
        ...
    }
    ...
}
</code></pre>
<h4 id="heading-redirect-http-traffic-to-https">Redirect HTTP traffic to HTTPs</h4>
<p>A very very very common scenario for web servers is to automatically redirect all clients who connect to the non-encrypted version of a website (HTTP traffic - i.e. server listening on port 80) to the safer encrypted version of the website (HTTPs traffic - i.e. server listening on port 443). Redirection is another easy thing to configure in Nginx:</p>
<pre><code class="lang-nginx"><span class="hljs-section">http</span> {
    ...
    <span class="hljs-section">server</span> {
        <span class="hljs-attribute">listen</span> <span class="hljs-number">80</span>;
        <span class="hljs-attribute">return</span> <span class="hljs-number">301</span> https://<span class="hljs-variable">$host</span><span class="hljs-variable">$request_uri</span>;
    }
    <span class="hljs-section">server</span> {
        ...
        <span class="hljs-attribute">listen</span> <span class="hljs-number">443</span>;
        ...
    }
    ...
}
</code></pre>
<p>What this config means is that Nginx will return a status code 301 (Moved Permanently) to all clients that connect to port 80. The redirection target will be a URL that uses the same host name and request URI but with <strong>https</strong> rather than <strong>http</strong> as the URL scheme. Browsers will automatically follow the redirection and move clients to the secure version of our website.</p>
]]></content:encoded></item></channel></rss>