<?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" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Ikhiloya Imokhai on Medium]]></title>
        <description><![CDATA[Stories by Ikhiloya Imokhai on Medium]]></description>
        <link>https://medium.com/@ikhiloyaimokhai?source=rss-1b0c24b58819------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*Jmh8MpzHh2IiLsRuzEiZuQ.jpeg</url>
            <title>Stories by Ikhiloya Imokhai on Medium</title>
            <link>https://medium.com/@ikhiloyaimokhai?source=rss-1b0c24b58819------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 20:45:39 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@ikhiloyaimokhai/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[WhatsApp Cloud Api + Payment Integration Observations]]></title>
            <link>https://ikhiloyaimokhai.medium.com/whatsapp-cloud-api-payment-integration-observations-a5cb4fabbdd3?source=rss-1b0c24b58819------2</link>
            <guid isPermaLink="false">https://medium.com/p/a5cb4fabbdd3</guid>
            <category><![CDATA[spring-boot]]></category>
            <category><![CDATA[meta]]></category>
            <category><![CDATA[whatsapp-cloud-api]]></category>
            <category><![CDATA[java]]></category>
            <category><![CDATA[paystack]]></category>
            <dc:creator><![CDATA[Ikhiloya Imokhai]]></dc:creator>
            <pubDate>Mon, 16 Mar 2026 08:04:25 GMT</pubDate>
            <atom:updated>2026-03-27T07:20:43.871Z</atom:updated>
            <content:encoded><![CDATA[<h4>Implementation observations from a WhatsApp Cloud API pilot in Nigeria, demonstrating conversational food ordering with integrated payment services</h4><figure><img alt="Architecture diagram showing WhatsApp Cloud API integration with a Spring Boot backend (Jollof Boss) and Nigerian payment service providers — Paystack for conversational commerce ordering and payments." src="https://cdn-images-1.medium.com/max/1000/1*NkChdPcB62nSul4r0SDffA.png" /></figure><p>Conversational commerce is already common in markets where messaging apps function as digital storefronts. In Nigeria, WhatsApp often serves as the primary channel through which small businesses interact with customers, share menus, and coordinate orders. However, unlike markets such as Brazil and India, where payments are natively integrated into WhatsApp, Nigerian businesses typically rely on third-party Payment Service Providers (PSPs). This separation requires developers to orchestrate messaging workflows, payment initiation, and transaction confirmations across multiple systems.</p><p>To explore how these systems interact in practice and serve our customers, we built a small pilot application called <strong>Jollof Boss</strong> — a WhatsApp-native food ordering service built with WhatsApp Cloud APIs and integrated with a Nigerian PSP (Paystack). The pilot instrumented the full<strong> order-to-payment</strong> lifecycle across <strong>pickup</strong> and <strong>delivery</strong> workflows, allowing us to observe latency, workflow coordination, and integration patterns between messaging infrastructure and payment services.</p><p>This article shares technical observations from that pilot, focusing on developer implementation patterns encountered when integrating WhatsApp Cloud messaging workflows, catalog-based ordering, webhook-driven order events, and external payment providers. The goal is to provide practical insights that may help developers design similar conversational commerce systems.</p><h3>System Overview</h3><p>The <strong>Jollof Boss system </strong>implemented a WhatsApp-native food ordering workflow covering the full order lifecycle:</p><pre>Customer → Menu → Order → Payment → Merchant/Customer Notification</pre><blockquote>The architecture connects messaging infrastructure with payment processing while keeping the entire experience within the WhatsApp conversation.</blockquote><h4>Technology Stack</h4><p>The pilot was implemented using the following components:</p><ol><li><strong>WhatsApp Cloud APIs:</strong> messaging, catalog menus, order events</li><li><strong>Spring Boot Backend:</strong> workflow orchestration and state management</li><li><strong>Paystack PSP integration</strong>: payment authorization and confirmation</li><li><strong>Webhook Architecture</strong>: event-driven transaction lifecycle</li></ol><h4>Architecture</h4><p>The system was implemented using an event-driven orchestration model as shown in the architecture diagram below.</p><figure><img alt="Architecture diagram showing WhatsApp Cloud API integration with a Spring Boot backend (Jollof Boss) and Nigerian payment service providers — Paystack for conversational commerce ordering and payments." src="https://cdn-images-1.medium.com/max/1000/1*NkChdPcB62nSul4r0SDffA.png" /><figcaption><em>Source: ChatGPT</em></figcaption></figure><h3>Observed Transaction Metrics</h3><p>During the pilot deployment, the backend recorded timestamps across each stage of the ordering workflow.</p><blockquote>These metrics are <strong>instrumentation observations from a small pilot deployment</strong>, not platform benchmarks.</blockquote><figure><img alt="Table showing messaging and backend performance metrics with WhatsApp dispatch time (1–3 seconds), backend processing (&lt;15 seconds), merchant notification (near-instant), payment processing time for pickup (105 seconds) and delivery (50 seconds), plus end-to-end transaction time of ~132 seconds for pickup orders and ~122 seconds for delivery orders." src="https://cdn-images-1.medium.com/max/1024/1*gHR8-DzRaFJLXa8obbhV5A.png" /><figcaption>source: ChatGPT</figcaption></figure><p>Across both workflows, <strong>payment completion represented the largest portion of transaction time</strong>. Most of this delay was attributable to user interaction steps such as authentication and payment interface navigation.</p><h3>Key Implementation Observations</h3><p>During the development of the conversational ordering workflow, several recurring integration patterns emerged.</p><p>These patterns reflect how developers currently coordinate messaging workflows, catalog data, and payment integrations when building conversational commerce systems on WhatsApp.</p><h4>1. Catalog Menus Are Application-Defined</h4><p>Interactive catalog menus using the WhatsApp product_list message type require developers to define menu sections and product identifiers directly within the API payload.</p><p>Example request:</p><pre>{<br>  &quot;type&quot;: &quot;interactive&quot;,<br>  &quot;interactive&quot;: {<br>    &quot;type&quot;: &quot;product_list&quot;,<br>    &quot;header&quot;: {<br>      &quot;type&quot;: &quot;text&quot;,<br>      &quot;text&quot;: &quot;Jollof Boss Menu&quot;<br>    },<br>    &quot;body&quot;: {<br>      &quot;text&quot;: &quot;Choose a meal&quot;<br>    },<br>    &quot;action&quot;: {<br>      &quot;catalog_id&quot;: &quot;158747027232639&quot;,<br>      &quot;sections&quot;: [<br>        {<br>          &quot;title&quot;: &quot;Jollof Rice Packs&quot;,<br>          &quot;product_items&quot;: [<br>            { &quot;product_retailer_id&quot;: &quot;4ru83njoue&quot; }<br>          ]<br>        }<br>      ]<br>    }<br>  }<br>}</pre><p>In practice, applications become responsible for managing:</p><ul><li>menu section structure</li><li>product groupings</li><li>product identifiers</li></ul><p>This often leads developers to maintain <strong>local representations of the catalog</strong> in order to compose menus dynamically.</p><h4>Possible Direction</h4><p>One potential improvement would allow merchants to define <strong>menu sections directly in Meta Commerce Manager</strong>, enabling applications to retrieve structured catalog layouts dynamically instead of composing menus in the application layer with hard-coded <strong><em>sections</em></strong> and <strong><em>product</em></strong><em> </em><strong><em>identifiers</em></strong>.</p><p>Merchants could also mark sections as <strong>active or out-of-stock</strong> without requiring application updates.</p><h4>2. Order Payloads Contain Product IDs but Not Product Names</h4><p>When a customer places an order, WhatsApp sends an <strong>order webhook event</strong> containing product identifiers, quantities, and prices.</p><p>Example payload:</p><pre>{<br>  &quot;catalogId&quot;: &quot;158747027232639&quot;,<br>  &quot;productItems&quot;: [<br>    {<br>      &quot;productRetailerId&quot;: &quot;q44n1fqt6z&quot;,<br>      &quot;quantity&quot;: 1,<br>      &quot;itemPrice&quot;: 13000,<br>      &quot;currency&quot;: &quot;NGN&quot;<br>    }<br>  ]<br>}</pre><p>However, the payload does not include <strong>human-readable product names</strong>.</p><p>To generate readable confirmations, such as:</p><pre>1 × 5-Litre Bowl Jollof Rice  <br>1 × Fried Chicken  <br>Total: NGN 17,000</pre><p>Applications typically resolve product names through one of two ways:</p><p><strong>Catalog API lookups </strong>resolving each productRetailerId through additional API calls or</p><p><strong>Local catalog caches</strong> for maintaining a synchronized local copy of the catalog.</p><p>Both approaches introduce additional complexity in the application layer.</p><h4>Possible Direction</h4><p>Including product names directly in the order payload could simplify order confirmation workflows.</p><p>Example:</p><pre>{<br>  &quot;productRetailerId&quot;: &quot;q44n1fqt6z&quot;,<br>  &quot;productName&quot;: &quot;5-Litre Bowl Jollof Rice&quot;,<br>  &quot;quantity&quot;: 1,<br>  &quot;itemPrice&quot;: 13000,<br>  &quot;currency&quot;: &quot;NGN&quot;<br>}</pre><p>This could reduce catalog lookup requests, backend orchestration logic and response latency in conversational workflows</p><h4>3. Merchant Inputs Often Require Free-Text Parsing</h4><p>Some conversational workflows require merchants to provide operational values before payment can proceed. In the delivery workflow, the merchant needed to provide a <strong>delivery fee</strong> based on the customer’s location.</p><p>Since WhatsApp Cloud APIs currently do not support structured merchant input fields, the pilot collected this value through formatted free-text messages.</p><p>Example format:</p><pre>DeliveryFee-PhoneNumber = 5000-234XXXXXXXX</pre><p>Backend workflow:</p><pre>Merchant message received<br>        ↓<br>Validate format<br>        ↓<br>Parse delivery fee<br>        ↓<br>Generate payment link</pre><p>While effective, this approach introduces:</p><ul><li>fragile parsing logic</li><li>strict message formatting requirements</li><li>additional backend validation logic</li></ul><h4>Possible Direction</h4><p>Structured <strong>input fields</strong> similar to a <strong>Call-To-Action (CTA) Button interactive template </strong>could simplify operational workflows that require merchant-provided values.</p><p>Conceptual example:</p><pre>{<br>  &quot;messaging_product&quot;: &quot;whatsapp&quot;,<br>  &quot;to&quot;: &quot;&lt;merchant_number&gt;&quot;,<br>  &quot;type&quot;: &quot;template&quot;,<br>  &quot;template&quot;: {<br>    &quot;name&quot;: &quot;request_delivery_fee&quot;,<br>    &quot;language&quot;: { &quot;code&quot;: &quot;en&quot; }<br>  },<br>  &quot;cta_input&quot;: {<br>    &quot;fields&quot;: [<br>      {<br>        &quot;name&quot;: &quot;delivery_fee&quot;,<br>        &quot;label&quot;: &quot;Delivery Fee (NGN)&quot;,<br>        &quot;type&quot;: &quot;number&quot;,<br>        &quot;required&quot;: true<br>      }<br>    ]<br>  }<br>}</pre><p>Structured inputs could support workflows requiring operational values such as:</p><ul><li>delivery fees</li><li>preparation times</li><li>stock confirmation</li><li>discount values</li></ul><h3>Final Thoughts</h3><p>This pilot demonstrates how conversational commerce workflows can be implemented using <strong>WhatsApp Cloud APIs and external payment providers</strong>.</p><p>Messaging delivery, webhook handling, and payment integrations operated reliably within an event-driven architecture.</p><p>The primary engineering effort centred on coordinating messaging events, catalog data, merchant inputs, and payment lifecycle events across systems. These patterns are not blockers, but they shape how developers currently design conversational commerce systems on WhatsApp.</p><p>The implementation observations documented here reflect a small pilot deployment but highlight patterns developers may encounter when building conversational commerce systems on WhatsApp.</p><h3>Project Context &amp; Further Reading</h3><p>This project forms part of the <strong>Jollof Boss conversational commerce pilot</strong>.</p><p>A broader discussion of the ecosystem implications is available in the <a href="https://techpoint.africa/brandpress/jollof-boss-launches-whatsapp-paystack-pilot-testing-chat-commerce-for-african-smes/">Techpoint Africa article</a>.</p><blockquote>These observations are also published in the <a href="https://github.com/Ikhiloya/whatsapp-cloudapi-payment-integration-observations"><strong>GitHub repository README</strong></a> for developers who prefer reviewing them in a repository format.</blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a5cb4fabbdd3" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Even in the Shadows]]></title>
            <link>https://ikhiloyaimokhai.medium.com/even-in-the-shadows-651b6a9171a9?source=rss-1b0c24b58819------2</link>
            <guid isPermaLink="false">https://medium.com/p/651b6a9171a9</guid>
            <category><![CDATA[longing]]></category>
            <category><![CDATA[poetry]]></category>
            <category><![CDATA[loneliness]]></category>
            <category><![CDATA[love]]></category>
            <category><![CDATA[poem]]></category>
            <dc:creator><![CDATA[Ikhiloya Imokhai]]></dc:creator>
            <pubDate>Tue, 04 Mar 2025 23:24:18 GMT</pubDate>
            <atom:updated>2025-03-04T23:24:18.600Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BlpIltJg8BUNm_RfRcop2A.png" /><figcaption><em>Image created using AI (DALL·E)</em></figcaption></figure><p>Even in the shadows,<br>you are here with me,<br>holding and guiding me<br>through the darkest of nights.</p><p>Even in the silence,<br>I hear your voice in my heart</p><p>When all is lost,<br>You keep whispering to me<br>about the future yet to unfold,<br>and the past we’ve shared -</p><p>The history we’ve had<br> is written in the scars of my heart,<br> and our memories<br> are deeply ingrained in them.</p><p>Even in the shadows,<br> I hear your footsteps,<br> and smell your perfume.</p><p>The warmth of your skin<br> comforts me in the coldest of nights,<br> but it’s never enough<br> to satisfy my longing for you — <br> my love and my light.</p><p>I am lost without you,<br> and I tremble and fall<br> a thousand times<br> without you by my side.</p><p>But even on my deepest lonely days,<br> you’re here with me,<br> walking by my side.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=651b6a9171a9" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Bon Voyage]]></title>
            <link>https://ikhiloyaimokhai.medium.com/bon-voyage-bc2d621bb935?source=rss-1b0c24b58819------2</link>
            <guid isPermaLink="false">https://medium.com/p/bc2d621bb935</guid>
            <category><![CDATA[poetry-writing]]></category>
            <category><![CDATA[death-and-dying]]></category>
            <category><![CDATA[poetry-on-medium]]></category>
            <category><![CDATA[tercet]]></category>
            <category><![CDATA[poetry]]></category>
            <dc:creator><![CDATA[Ikhiloya Imokhai]]></dc:creator>
            <pubDate>Fri, 09 Aug 2024 07:36:29 GMT</pubDate>
            <atom:updated>2024-08-09T07:36:29.635Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZewtbcUMraCsPDw75wNdmw.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/@labrum777?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash">Mike Labrum</a> on <a href="https://unsplash.com/photos/low-angle-photo-of-lightened-candles-fvl4b1gjpbk?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash">Unsplash</a></figcaption></figure><p>At yonder bank, we shall meet</p><p>Oh fairest of them all</p><p>Bon voyage!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bc2d621bb935" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to Deploy a Spring Boot App with Docker to DigitalOcean App Platform]]></title>
            <link>https://ikhiloyaimokhai.medium.com/how-to-deploy-a-spring-boot-app-with-docker-to-digitalocean-app-platform-6789a315e69a?source=rss-1b0c24b58819------2</link>
            <guid isPermaLink="false">https://medium.com/p/6789a315e69a</guid>
            <category><![CDATA[spring-boot]]></category>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[digitalocean]]></category>
            <category><![CDATA[digitalocean-app-platform]]></category>
            <category><![CDATA[java]]></category>
            <dc:creator><![CDATA[Ikhiloya Imokhai]]></dc:creator>
            <pubDate>Mon, 08 Jan 2024 01:43:50 GMT</pubDate>
            <atom:updated>2024-11-29T11:09:06.561Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/790/0*8gSC9Ch5P42IAG2D" /></figure><h3>Introduction</h3><p>DigitalOcean App platform is a Platform-as-a-service (Paas) that allows developers to publish code directly to DigialOcean servers without worrying about the underlying infrastructure. The Platform supports two ways to build an image for your app: <a href="https://buildpacks.io/">Cloud Native Buildpacks</a> and <a href="https://docs.docker.com/engine/reference/builder/">Dockerfiles</a>.</p><p>When an app is deployed to the App Platform either via GitHub or DockerHub, it defaults to using Dockerfile if one is present in the root directory or specified app spec. Otherwise, the App Platform checks your code to determine what language or framework it uses. If it supports the language or framework, it chooses an appropriate resource type and uses the proper buildpack to build the app and deploy a container.</p><p>Java/Spring boot is not supported out of the box on the App platform hence developers rely on a Dockerfile for deployment. This sometimes could be a daunting challenge as there is little documentation about it and it depends on the level of Docker expertise of the developer.</p><p>In this article, you’ll learn how to deploy a Spring Boot app with a Docker to the DigitalOcean App platform. You’ll build a Spring Boot app that saves and retrieves messages from a postgres database, set instructions in the Dockerfile, push the code to a GitHub repository, then configure the application as a DigitalOcean app, attach a PostgreSQL database and deploy the app.</p><h3>Prerequisites</h3><p>For this tutorial, you’ll need the following:</p><ul><li><a href="https://www.oracle.com/java/technologies/downloads/">Java</a> 8 version or greater installed on your local machine.</li><li>Git installed on your local machine. You can follow the tutorial <a href="https://www.digitalocean.com/community/tutorials/how-to-contribute-to-open-source-getting-started-with-git">Contributing to Open Source: Getting Started with Git</a> to install and set up Git on your local computer.</li><li>A local machine with Docker installed. Your local machine can be running any Linux distribution, or even Windows or macOS. For Windows and macOS, install Docker using the <a href="https://www.docker.com/products/docker-desktop/">official installer</a>. If you have Ubuntu 16.04 running on your local machine, but Docker is not installed, see <a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-16-04">How To Install and Use Docker on Ubuntu 16.04</a> for instructions.</li><li>A local machine with Maven installed. Your local machine can be running any Linux distribution, or even Windows or macOS. For macOS, <a href="https://www.digitalocean.com/community/tutorials/install-maven-mac-os">See How to install Maven on Mac OS by Pankaj</a>. For Linux <a href="https://www.digitalocean.com/community/tutorials/install-maven-linux-ubuntu">See How to install Maven on Linux (Ubuntu) by Pankaj</a>.</li><li>Spring Boot Command Line Tool which you can use to quickly prototype with spring without an IDE. <a href="https://docs.spring.io/spring-boot/docs/current/reference/html/getting-started.html#getting-started.installing.cli">See Installing the Spring Boot CLI</a> for details on how to install the CLI, Maven, Gradle and an spring boot example.</li><li>Spring Boot based development projects by reading <a href="https://www.digitalocean.com/community/tutorials/spring-boot-tutorial">Spring Boot Tutorial by Rambabu Posa</a>.</li><li>Spring Data JPA by reading <a href="https://www.digitalocean.com/community/tutorials/spring-data-jpa">Spring Data JPA by Pankaj</a></li><li>Postgresql Database by reading <a href="https://www.digitalocean.com/community/tutorials/what-is-postgresql">What is PostgreSQL by Mark Drake</a>. See <a href="https://www.digitalocean.com/community/tutorial_collections/how-to-install-and-use-postgresql">How To Install and Use PostgreSQL</a> for details on how to install and use PostgreSQL.</li><li>A <a href="https://www.digitalocean.com/">DigitalOcean</a> account.</li><li>An account on GitHub, which you can create by going to the <a href="https://github.com/">Create your Account page</a>.</li></ul><h3>Step 1 — Create a Spring Boot Application</h3><p>In this step, you’ll create a Spring Boot application using the Spring Boot <a href="https://docs.spring.io/spring-boot/docs/current/reference/html/getting-started.html#getting-started.installing">CLI</a>. You should explore the various installation options.</p><p><em>Note: If you encounter the error below, please install OpenJDK 17</em><br>‘LinkageError` mismatch (that the JRE was being set up with Java 11/bytecode 55 but needed to be run with Java 17/bytecode 61</p><p>To start, initialize a new Spring Boot project using the init command. The init command lets you create a new project by using the <a href="https://start.spring.io/">start.spring.io</a> without leaving the shell, as shown below:</p><pre>spring init --build=maven --java-version=17 --dependencies=web,data-jpa,postgresql spring-boot-docker</pre><p>The --build flag indicates the build type which could be maven or gradle. The --java-version flag indicates the preferred Java version. The --dependencies flag shows the list of project dependencies. This project uses the spring-boot-starter-web, spring-boot-starter-data-jpa and postgresql database driver.</p><p>Essentially, the above command creates a spring-boot-docker directory with a Maven-based project.</p><p>The output of the init command indicating the project creation is shown below:</p><pre>Using service at https://start.spring.io<br>Project extracted to &#39;/Users/your directory/spring-boot-docker&#39;</pre><p>You can list the capabilities of the Spring Boot CLI service by using the --list flag to list all dependencies as shown below.</p><pre>spring init --list</pre><p>At this point, your project is set up in a new directory. Switch to the new directory:</p><pre>cd spring-boot-docker</pre><p>You have now created a springboot project and you are currently in the project folder. In the next section, you’ll add a custom Java class managed by Spring Data JPA that will store data in the postgresql database.</p><h3>Step 2 — Add Model Class</h3><p>In this step, you’ll create a Message Java class that will hold messages for our application. the Message class will contain two fields: Id and body.</p><p>Since you’re in your project directory, navigate to the project root directory using this command:</p><pre>cd src/main/java/com/example/springbootdocker</pre><p>Create a new Message.java file:</p><pre>touch Message.java</pre><p>Open the Message.java file:</p><pre>nano Message.java</pre><p>Add the Message class to hold the ID and body of the message:</p><pre>package com.example.springbootdocker;<br><br>//import javax.persistence.*;<br>import jakarta.persistence.*;<br>@Entity<br>@Table(name = &quot;message&quot;)<br>public class Message {<br>    @Id<br>    @GeneratedValue(strategy = GenerationType.AUTO)<br>    @Column(name = &quot;id&quot;, nullable = false)<br>    private Long id;<br>    private String body;<br>    public Long getId() {<br>        return id;<br>    }<br>    public void setId(Long id) {<br>        this.id = id;<br>    }<br>    public String getBody() {<br>        return body;<br>    }<br>    public void setBody(String body) {<br>        this.body = body;<br>    }<br>}</pre><p>The above is a simple Message.java class that has the Id and body as the only fields with their corresponding getter and setter methods.</p><p>If you are using Java 17, you need to change the import statement from import javax.persistence.*; to import jakarta.persistence.*.</p><p>The @Entity annotation specifies that the Message class is an entity and is mapped to a database table.</p><p>The @Id annotation specifies the primary key of the entity and the @GeneratedValue provides for the specification of generation strategies for the values of primary keys.</p><p>You have now created a Message class mapped to the postgres database that will hold messages. In the next step, you’ll create a repository layer that will provide the mechanism for storage, retrieval, search, update and delete operations on the Message entity.</p><h3>Step 3 — Add Repository Layer</h3><p>In this step, you’ll create a Java interface annotated with the @Repository annotation. This annotation is used to indicate that the class provides the mechanism for storage, retrieval, search, update and delete operations on the Message entity. For more details on @Repository annotation, see <a href="https://www.digitalocean.com/community/tutorials/spring-repository-annotation">Spirng @Repository Annotation by Pankaj</a>.</p><p>In your project directory, create a MessageRepository.java file:</p><pre>touch MessageRepository.java</pre><p>Open the MessageRepository.java file:</p><pre>nano MessageRepository.java</pre><p>Add the MessageRepository Interface which extends JpaRepository:</p><pre>package com.example.springbootdocker;<br>import org.springframework.data.jpa.repository.JpaRepository;<br>import org.springframework.stereotype.Repository;<br><br>@Repository<br>public interface MessageRepository extends JpaRepository&lt;Message, Long&gt; {}</pre><p>The MessageRepository interface extends the JpaRepository which takes the Message class created in the previous step and the Long data type since the Id specified in the Message class is of type Long.</p><p>You have now created the repository layer for data accesss. In the next step, you’ll create a controller layer that’ll expose methods to save and retrieve messages via a RestFul webservice call.</p><h3>Step 4 — Add Controller Layer</h3><p>In this step, you’ll create a spring boot controller class that will expose two methods to save and retrieve messages from the database.</p><p>Still in your project directory, create a MessageController file:</p><pre>touch MessageController.java</pre><p>Open the MessageController.java file:</p><pre>nano MessageController.java</pre><p>Add the MessageController classs which exposes two methods to save and retrieve messages.</p><pre>package com.example.springbootdocker;<br><br>import org.springframework.beans.factory.annotation.Autowired;<br>import org.springframework.web.bind.annotation.*;<br>import java.util.Optional;<br>@RestController<br>@RequestMapping(&quot;/message&quot;)<br>public class MessageController {<br> <br>    @Autowired<br>    MessageRepository messageRepository;<br>    @GetMapping(&quot;/{id}&quot;)<br>    public Optional&lt;Message&gt; getMessage(@PathVariable(&quot;id&quot;) Long id) {<br>        return messageRepository.findById(id);<br>    }<br>    @PostMapping<br>    public Message saveMessage(@RequestBody Message message) {<br>        return messageRepository.save(message);<br>    }<br>}</pre><p>The @RestController annotation is a convenience annotation that is itself annotated with @Controller and @ResponseBody. This annotation is applied to a class to mark it as a request handler. Spring RestController annotation is used to create RESTful web services using Spring MVC. Spring RestController takes care of mapping request data to the defined request handler method. Once response body is generated from the handler method, it converts it to JSON or XML response. See <a href="https://www.digitalocean.com/community/tutorials/spring-restcontroller">Spring RestController by Pankaj</a> for more details on @RestController.</p><p>So far, you have been able to create a Message Entity, Message Repository layer as well as the Message controller class. To be able to save and retrieve messages per the preceeding steps, you need to add some database configurations for the postgres database. You’ll achieve this in the next step.</p><p><em>Note: You might need to change the import statements to match yours.</em></p><h3>Step 5 — Add Postgres Database Configuration</h3><p>In this step, you’ll add database credentials to the application.properties file which was created in step 1. The database credentials ensures that a secure database connection can be made to the database.</p><p>Navigate to the resources directory from the project root directory using this command:</p><pre>cd src/main/resources</pre><p>Open the application.properties file.</p><pre>nano application.properties</pre><p>Add the following properties:</p><pre>spring.datasource.url=jdbc:postgresql://localhost:5432/&lt;changeme&gt;<br>spring.datasource.username=&lt;changeme&gt;<br>spring.datasource.password=&lt;changeme&gt;<br>spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect<br>spring.jpa.hibernate.ddl-auto=update<br>spring.jpa.show-sql=true</pre><p><em>Note: You need to change the credentials for your database.</em></p><p>The spring.datasource.url specifies the host, port and the database name. The spring.datasource.username refers to the name of your database and the spring.datasource.password refers to the password.</p><p>spring.jpa.properties.hibernate.dialect makes hibernate generate better SQL for the chosen database.</p><p>spring.jpa.hibernate.ddl-auto is a Hibernate feature that controls the behaviour in a more fine-grained way. it could be create, create-drop, validate or update</p><p>spring.jpa.show-sql shows the sql in the logs for debugging.</p><p>In this step, you’ve added the necessary database configuration to the application.properties file. You&#39;re now ready to test your application locally. In the next step, you&#39;ll test your application.</p><h3>Step 6 — Test Spring Boot App Locally</h3><p>In this step, you’ll test the save and retrieve methods exposed in the controller in step 4.</p><p>For this, you’ll use curl command.</p><p>To run the app, navigate to the project root directory and run the command below:</p><pre>mvn spring-boot:run</pre><p>The output of the command indicating that the project is running is shown below:</p><pre>o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path &#39;&#39;<br>2022-11-24 08:29:03.159  INFO 50443 --- [           main] c.e.springbootdocker.DemoApplication     : Started DemoApplication in 1.399 seconds (JVM running for 1.526)</pre><p>To save a message, run the command below in a new terminal shell:</p><pre>curl -X POST http://localhost:8080/message -H &#39;Content-Type:  application/json&#39; -d &#39;{&quot;body&quot;:&quot;Hello world, I am making progress so far&quot;}&#39;</pre><p>The above request saves a new message record to the database, the output is shown below:</p><pre><br>{&quot;id&quot;:1,&quot;body&quot;:&quot;Hello world, I am making progress so far&quot;}%</pre><p>To retrieve a message by id, run the command below:</p><pre>curl -X GET http://localhost:8080/message/1</pre><p>The above request retrieves an existing message by id from the database, the output is shown below:</p><pre>{&quot;id&quot;:1,&quot;body&quot;:&quot;Hello world, I am making progress so far&quot;}%</pre><p>So far, you have created an application that’s working locally. In the next step, you’ll create a Dockerfile that will handle the build and deployment process.</p><h3>Step 7 — Add a Dockerfile</h3><p>In this step, you’ll add a Dockerfile to manage the build and deployment process.</p><p>Docker is an open-source platform for building, deploying, sharing and managing containerized applications. Developers can install the apps from a single package and get them up and running in minutes.</p><p>To Dockerize our Spring Boot App, a Dockerfile is used. The Dockerfileis a text document containing all the commands and instructions a user could call on the command line to assemble an image. A Docker image is composed of a stack of layers, each representing an instruction in our Dockerfile.</p><p>DigitalOcean App platform will rely on the Dockerfile for build instructions during deployment.</p><p>See this <a href="https://docs.docker.com/engine/reference/builder/">article</a> for more information on Docker and this <a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-22-04">article</a> for how to install Docker.</p><p>For this application, you’ll create a Dockerfile in the app&#39;s root directory using the following command:</p><pre>touch Dockerfile</pre><p>Open the Dockerfile:</p><pre>nano Dockerfile</pre><p>Add the following commands to the Dockerfile in order to build the image for the application.</p><pre>FROM eclipse-temurin:11-jdk-jammy as builder<br>WORKDIR /opt/app<br>COPY .mvn/ .mvn<br>COPY mvnw pom.xml ./<br>RUN ./mvnw dependency:go-offline<br>COPY ./src ./src<br>RUN ./mvnw clean install -DskipTests<br><br>FROM eclipse-temurin:11-jre-jammy<br>WORKDIR /opt/app<br>EXPOSE 8080<br>COPY --from=builder /opt/app/target/*.jar /opt/app/*.jar<br>ENTRYPOINT [&quot;java&quot;,&quot;-Dspring.profiles.active=prod&quot;, &quot;-jar&quot;, &quot;/opt/app/*.jar&quot; ]</pre><p>The above Dockerfile uses a <a href="https://docs.docker.com/build/building/multi-stage/">multi-stage</a> build process by copying results from one image to another. The first stage extracts the dependencies while the second stage copies the extracted dependencies to the final image.</p><p>The command FROM eclipse-temurin:11-jdk-jammy as builder defines the base image from the Eclipse Temurin project, one of the most popular official images with a build-worthy JDK. The Eclipse Temurin project provides code and processes that support the building of runtime binaries and associated technologies.</p><p><em>Note: that </em><em>Java 11 is specified for the app build, which matches the Java version in step 1</em></p><p>The second command WORKDIR /opt/app creates a working directory of a Docker container at any given time. Any RUN, CMD, ADD, COPY, or ENTRYPOINT command will be executed in the specified working directory.</p><p>COPY .mvn/ .mvn command copies files from a local source location to a destination in the Docker container.</p><p>RUN ./mvnw dependency:go-offline runs the spring boot application. The resulting committed image will be used for the next step in the Dockerfile. dependency:go-offline goal resolves all dependencies including plugins and reports and their dependencies. After running this goal, you can safely work in offline mode.</p><p>RUN ./mvnw clean install tells Maven to do the clean phase in each module before running the install phase for each module. This clears any compiled files you have, making sure that you&#39;re really compiling each module from scratch.</p><p>Notice that the first image is labelled builder. We use it to run eclipse-temurin:11-jdk-jammy, build the JAR, and unpack it.</p><p>The second layer of the multi-stage build process contain the build configuration and the source code for the application while the earlier layer contain the complete Eclipse JDK image. This small optimization saves us from copying the target directory to a Docker image.</p><p>Finally, an ENTRYPOINT lets you configure a container that runs as an executable. It corresponds to your java -jar target/*.jar command.</p><p>For more information on Dockerfile commands, see Docker Explained: <a href="https://www.digitalocean.com/community/tutorials/docker-explained-using-dockerfiles-to-automate-building-of-images">Using Dockerfiles to Automate Building of Images by O.S Tezer</a>.</p><p>So far you have created a Dockerfile that contains the necessary commands needed to build a Docker image. In the next step, you&#39;ll build the Docker image locally.</p><h3>Step 8 — Build Docker Image</h3><p>In this step, you’ll build the Docker image locally based on the set of commands as specified in the Dockerfile.</p><p>To build a docker image, ensure your `Docker’ instance is running before you run this command:</p><pre>docker build -t spring-boot-docker .</pre><p>The docker build command builds Docker images from the Dockerfile and a context. A build&#39;s context is the set of files locates in the specified PATH or URL.</p><p>The output of the build command indicating that the successful build of the Docker image is shown below:</p><pre>[+] Building 846.9s (16/16) FINISHED                                                                                                                                                                                  <br> =&gt; [internal] load build definition from Dockerfile                                                                                                                                                             0.0s<br> =&gt; =&gt; transferring dockerfile: 37B                                                                                                                                                                              0.0s<br> =&gt; [internal] load .dockerignore                                                                                                                                                                                0.0s<br> =&gt; =&gt; transferring context: 2B                                                                                                                                                                                  0.0s<br> =&gt; [internal] load metadata for docker.io/library/eclipse-temurin:11-jre-jammy                                                                                                                                  4.7s<br> =&gt; [internal] load metadata for docker.io/library/eclipse-temurin:11-jdk-jammy                                                                                                                                  4.7s<br> =&gt; [stage-1 1/3] FROM docker.io/library/eclipse-temurin:11-jre-jammy@sha256:94a23660199bda9133dff36ef811446d9866859946de3b6fea03ba3dca6618bc                                                                    0.0s<br> =&gt; [internal] load build context                                                                                                                                                                                0.0s<br> =&gt; =&gt; transferring context: 1.26kB                                                                                                                                                                              0.0s<br> =&gt; [builder 1/7] FROM docker.io/library/eclipse-temurin:11-jdk-jammy@sha256:ff15d0a337affefa41a3619cb5d8626c11e0d38c84e85ac3b0fafc5f86ff68e9                                                                    0.0s<br> =&gt; CACHED [stage-1 2/3] WORKDIR /opt/app                                                                                                                                                                        0.0s<br> =&gt; CACHED [builder 2/7] WORKDIR /opt/app                                                                                                                                                                        0.0s<br> =&gt; CACHED [builder 3/7] COPY .mvn/ .mvn                                                                                                                                                                         0.0s<br> =&gt; CACHED [builder 4/7] COPY mvnw pom.xml ./                                                                                                                                                                    0.0s<br> =&gt; [builder 5/7] RUN ./mvnw dependency:go-offline                                                                                                                                                             819.9s<br> =&gt; [builder 6/7] COPY ./src ./src                                                                                                                                                                               0.0s<br> =&gt; [builder 7/7] RUN ./mvnw clean install -DskipTests                                                                                                                                                          21.6s <br> =&gt; [stage-1 3/3] COPY --from=builder /opt/app/target/*.jar /opt/app/*.jar                                                                                                                                       0.1s <br> =&gt; exporting to image                                                                                                                                                                                           0.1s <br> =&gt; =&gt; exporting layers                                                                                                                                                                                          0.1s <br> =&gt; =&gt; writing image sha256:0f5e9737106234ca975fd9b38e50e5e461872eb959b8fde1854adbfebb74b415                                                                                                                     0.0s <br> =&gt; =&gt; naming to docker.io/library/spring-boot-docker                                                                                                                                                            0.0s</pre><p>In this step, you have successfully built a Docker image locally. In the next step, you&#39;ll push the app to a GitHub repository.</p><h3>Step 9 — Push Code to GitHub</h3><p>To deploy your app, App platform retrieves your source code from a specified Github repository.</p><p>Login to your GitHub acocunt and create a new public or private repository called spring-boot-docker. This automatically initializes the repository with git which enable you to push the code directky to GitHub.</p><p>To initialize the local repository, add the command:</p><pre>git init</pre><p>You’ll need to add the repository to your local project using this command:</p><pre>git remote add origin https://github.com/your_name/spring-boot-docker</pre><p>Next, switch to the main branch using the following command:</p><pre>git branch -M main</pre><p>Add all the project files to git using this command:</p><pre>git add .</pre><p>After adding the files, commit the files using:</p><pre>git commit -m &quot;commit message&quot;</pre><p>Finally, push to code to your Github repository using this command:</p><pre>git push -u origin main</pre><p>Once prompted, enter your GitHub credentials. If done successfully, you’ll receive a success message similar to:</p><pre>Enumerating objects: 29, done.<br>Counting objects: 100% (29/29), done.<br>Delta compression using up to 10 threads<br>Compressing objects: 100% (21/21), done.<br>Writing objects: 100% (29/29), 60.07 KiB | 15.02 MiB/s, done.<br>Total 29 (delta 0), reused 0 (delta 0), pack-reused 0<br>To https://github.com/Ikhiloya/spring-boot-docker.git<br> * [new branch]      main -&gt; main<br>Branch &#39;main&#39; set up to track remote branch &#39;main&#39; from &#39;origin&#39;.</pre><p>In this step, you created a GitHub repository and pushed your code to that repository. In the next step, you’ll create a DigitalOcean app and deploy from your GitHub repository.</p><h3>Step 10 — Deploy App to DigitalOcean App Platform</h3><p>In this step, you’ll learn how to deploy your project from the GitHub repository to DigitalOcean App platform.</p><p>To start, login to your DigitalOcean account, click on the Create button and select Apps from the drop down menu:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*86jXDVSuK4XXvgFv" /></figure><p>You’ll be prompted to link your preferred GitHub repository which will require you to authorize DigitalOcean to be able to access your repositories. Choose the spring-boot-docker repository you created earlier and click on next as shown below:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*8bP0Trfhw_T62jAz" /></figure><p>You’ll notice that App platform has detected the Dockerfile from the project repository. You can choose Edit plan if you want to change the plan.</p><p>Click Next to proceed to add Environment Variables which enables all resources to have access to variables at build time and runtime.</p><p>For now, you don’t need to add anything to the Environment variables as you have not attached a database which will be discussed later.</p><p>Click Next to select your preferred app name and region that’s closest to your physical location.</p><p>Click Next again to review your app’s resources after which you can click on Create Resources to get your app running.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*zdHCs1zp3R8j7LSY" /></figure><p>This effectively deploys the application using the Dockerfile you created. Since you have not attached a database, you&#39;ll have a database connection error:</p><pre>org.postgresql.util.PSQLException: connection to localhost:5432 refused .....</pre><p>To fix this error, you need to attach a database and add the database credentials as environment variables.</p><h3>Step 11 — Attach a Postgresgl Database</h3><p>In this step, you’ll attach a database and add the database connection parameters as Environment variables. This will effectively eliminate the database connection error observed in the previous step.</p><p>Click the Create button and select Database; select a postgres database and your preferred plan.</p><p>Click Next to view the database credentials.</p><p>You need to add the database credentials as environment variables as key-value pairs. Navigate to the Settings tab of your app, click on the app component and edit the Environment Variables with the following:</p><pre>SPRING_DATASOURCE_URL=jdbc:postgresql://&lt;host&gt;:&lt;port&gt;/&lt;dbname&gt;?sslmode=require<br>SPRING_DATASOURCE_USERNAME=&lt;changeme&gt;<br>SPRING_DATASOURCE_PASSWORD=&lt;changeme&gt;</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*UUyALMrEwCeB5EbL" /></figure><p>Click on save and this will trigger the deployment process. Once the deployment is successful, you’ll see an output like this:</p><pre>o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path &#39;&#39;<br>2022-11-24 08:29:03.159  INFO 50443 --- [           main] c.e.springbootdocker.DemoApplication     : Started DemoApplication in 1.399 seconds (JVM running for 1.526)</pre><p>In this step you have learnt how to attach a database and add the database credentials as environment variables. In the next step, you’ll be able to test using the live url.</p><h3>Step 12 — Test App using Live Url</h3><p>In this step, you’ll obtain the app’s live url and test the endpoints to save and retrieve messages.</p><p>Click on the Apps tab, navigate to your app and copy the app’s live url.</p><p>To save a message, use the following command:</p><pre>curl -X POST https://your-app-url/message -H &#39;Content-Type:  application/json&#39; -d &#39;{&quot;body&quot;:&quot;Hello world, I am making progress so far&quot;}&#39;</pre><p>The above request saves a new message record to the database, the output is shown below:</p><pre>{&quot;id&quot;:4,&quot;body&quot;:&quot;Hello world, I am making progress so far&quot;}%</pre><p>To retrieve a message by id, run the command below:</p><pre>curl -X GET https://your-app-url/message/4</pre><p>The above request retrieves an existing message by id from the database, the output is shown below:</p><pre>{&quot;id&quot;:4,&quot;body&quot;:&quot;Hello world, I am making progress so far&quot;}%</pre><p>So far, you have been able to test the app using the app’s live URL.</p><h3>Conclusion</h3><p>In this tutorial, you have learnt how to deploy a Spring Boot App using a Dockerfile to DigitalOcean App platform, attach a database and test using the live URL.</p><p>You can find the source code for this project in this <a href="https://github.com/Ikhiloya/spring-boot-docker">GitHub repository</a>.</p><p><a href="https://github.com/Ikhiloya/spring-boot-docker">GitHub - Ikhiloya/spring-boot-docker</a></p><p><em>You can find the Markdown version of this article in this </em><a href="https://gist.github.com/d1d0d034c63199eed2c67a9a1a400152.git"><em>gist</em></a><em>.</em></p><p>For further information, please visit the official <a href="https://www.digitalocean.com/docs/app-platform/">App Platform product documentation</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6789a315e69a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Stay With Me]]></title>
            <link>https://ikhiloyaimokhai.medium.com/stay-with-me-1562e1209bbb?source=rss-1b0c24b58819------2</link>
            <guid isPermaLink="false">https://medium.com/p/1562e1209bbb</guid>
            <category><![CDATA[love]]></category>
            <category><![CDATA[hopeless-romantic]]></category>
            <category><![CDATA[loneliness]]></category>
            <category><![CDATA[hopes-and-fears]]></category>
            <category><![CDATA[love-letters]]></category>
            <dc:creator><![CDATA[Ikhiloya Imokhai]]></dc:creator>
            <pubDate>Sat, 13 Aug 2022 12:23:04 GMT</pubDate>
            <atom:updated>2022-08-13T12:24:13.217Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*EDk0yLA9KUAZalmMLEUF1A.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/@anthonyintraversato?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Anthony Intraversato</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><p>I yearn for your presence</p><p>To be with you one last time</p><p>My soul beckons for your warm embrace</p><p>To lock my lips to every spot on your face</p><p>Don’t go! Please stay with me.</p><p>I can feel your presence in my heart</p><p>You’re in my every thought</p><p>Although you belong to yours</p><p>I wait to steal one final glance at you</p><p>Don’t go! Please stay with me.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1562e1209bbb" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Annotation-Based Offline Caching in Retrofit]]></title>
            <link>https://medium.com/swlh/annotation-based-offline-caching-in-retrofit-d7dbd775ac74?source=rss-1b0c24b58819------2</link>
            <guid isPermaLink="false">https://medium.com/p/d7dbd775ac74</guid>
            <category><![CDATA[android-cache]]></category>
            <category><![CDATA[retrofit2]]></category>
            <category><![CDATA[okhttp]]></category>
            <category><![CDATA[annotations]]></category>
            <category><![CDATA[android-app-development]]></category>
            <dc:creator><![CDATA[Ikhiloya Imokhai]]></dc:creator>
            <pubDate>Thu, 29 Oct 2020 06:41:45 GMT</pubDate>
            <atom:updated>2020-10-29T23:21:02.976Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zcm7fBm3snRiAtnQ_qZ8zQ.png" /></figure><p>An annotation<em> </em>is a form of syntactic metadata that provides data about a program but is not part of the program itself. <a href="https://docs.oracle.com/javase/tutorial/java/annotations/">Annotations</a> have no direct effect on the operation of the code they are added to.</p><p>In Java, annotations can also be embedded in and read from <a href="https://en.wikipedia.org/wiki/Java_class_file">Java class files</a> generated by the <a href="https://en.wikipedia.org/wiki/Java_compiler">Java compiler</a> which allows annotations to be retained by the <a href="https://en.wikipedia.org/wiki/Java_virtual_machine">Java virtual machine</a> at <a href="https://en.wikipedia.org/wiki/Run_time_(program_lifecycle_phase)">run-time</a> and read via <a href="https://en.wikipedia.org/wiki/Reflection_(computer_science)">reflection</a>.</p><p>Annotations can be used for the following:</p><ul><li>Detect errors or suppress warnings e.g <em>@Override and</em><em>@SuppressWarnings.</em></li><li>Compile-time and deployment-time processing e.g code generation tools.</li><li>Runtime processing.</li></ul><p>This article shows how to leverage custom annotations to mark specific web service requests for caching in Retrofit. This is because we usually have a single Retrofit instance to make web service requests to the server. Therefore, it is necessary to devise a means of identifying requests that should be <strong>cached</strong>.</p><h4>Use Case</h4><p>Consider an app that displays a list of payment types (Cash, Bank Transfer, Bitcoin, Card, e.t.c.) which is fetched from a server.</p><p>Since the list of payment types rarely change, it makes sense to cache this request to reduce the number of backend calls for this resource. This and such-like requests would qualify for caching.</p><h4>The Challenge</h4><p>Having identified requests that qualify for caching, how do we ensure that only such-like requests are retrieved from the cache when there is no internet connection?</p><blockquote>That is, how do we “mark” such requests as <strong>Cacheable</strong>.</blockquote><h4>The Proposed Solution</h4><p>To solve this problem, the following are needed:</p><ol><li>A cache to store and retrieve the resource.</li><li>A network interceptor to cache requests when the network is available.</li><li>A custom annotation to mark requests as <strong>Cacheable</strong>.</li><li>An offline interceptor to cache requests when the network is unavailable.</li><li>An OKHttp client and a Retrofit instance.</li></ol><h4><strong>Step 1: Create a Cache</strong></h4><p>We simply create a cache of size 5MB to store and retrieve the server’s resources.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bc32f1409cb80b4afaee8b3fcd3d94ae/href">https://medium.com/media/bc32f1409cb80b4afaee8b3fcd3d94ae/href</a></iframe><blockquote>Please see the reference section for resources on caching in android.</blockquote><h4>Step 2: Create a Network Interceptor</h4><p>This interceptor is called when there is a network connection and helps to fetch cached resources without making a network call to the server.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f3e87646e23e6e05a9ae8f6915ccb807/href">https://medium.com/media/f3e87646e23e6e05a9ae8f6915ccb807/href</a></iframe><p>The max-age is set to 5 seconds which specifies the maximum age of a cached response. If the cached response’s age exceeds maxAge, it will not be used and a network request will be made.</p><p><em>To learn more about interceptors please see the links below:</em></p><ul><li><a href="https://square.github.io/okhttp/interceptors/"><em>OkHttp Interceptors</em></a></li><li><a href="https://medium.com/swlh/okhttp-interceptors-with-retrofit-2dcc322cc3f3"><em>OkHttp Interceptors with Retrofit</em></a></li></ul><h4>Step 3: Create Custom Annotation</h4><p>In this step, create a custom runtime annotation with the target set to Function. This annotation would be added to specific requests which would ensure that they are cached. The annotation is added to the Invocation class and made available at runtime.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/200790efa81648b43999a4faac7a1003/href">https://medium.com/media/200790efa81648b43999a4faac7a1003/href</a></iframe><p>@Retention(AnnotationRetention.RUNTIME) ensures that the annotation is available at runtime.</p><p>@Target(AnnotationTarget.FUNCTION) ensures that the annotation is applied to methods or functions such as the web service request abstract method.</p><p>@MustBeDocumented is a meta-annotation that ensures the annotation is included in the generated documentation for the element to which the annotation is applied.</p><blockquote>Retrofit automatically adds an invocation to each OkHttp request as a tag. You can retrieve the invocation in an OkHttp interceptor for metrics and monitoring.</blockquote><h4>Step 4: Create an Offline Interceptor</h4><p>This interceptor is called when there is no network connection. It intercepts the request and fetches the cached resource without making a call to the server.</p><p>In this interceptor, we check if the particular request is marked for caching. That is, we check if the request contains the <strong>Cacheable </strong>annotation. If the request contains the annotation, the stale cached response is retrieved otherwise it is not.</p><p>The first step is to retrieve the Invocation object from the request by tag. This is because Retrofit automatically adds an invocation to each OkHttp request as a tag.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d64d3446548468d11f35c8c585052b54/href">https://medium.com/media/d64d3446548468d11f35c8c585052b54/href</a></iframe><p>Then, we can get the <strong>Cacheable</strong> annotation from the invocation instance.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/23e850c9d64c589a759fac01530f1f1c/href">https://medium.com/media/23e850c9d64c589a759fac01530f1f1c/href</a></iframe><p>Lastly, we check if the annotation is of type <strong>Cacheable </strong>before caching the request.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/7ae39fa34a175538585ed24d49a6ab1b/href">https://medium.com/media/7ae39fa34a175538585ed24d49a6ab1b/href</a></iframe><p>The full offline interceptor code is shown below:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/80b9c769479319f2feb68b7fd69f20c6/href">https://medium.com/media/80b9c769479319f2feb68b7fd69f20c6/href</a></iframe><p>Responses can be retrieved when the freshness lifetime has not exceeded the maxStale duration (7 days in this case). Once the maxStale duration is exceeded, responses can no longer be retrieved.</p><h4>Step 5: Create OKHttp client and a Retrofit instance</h4><p>The final step is to create the OKHttp client and retrofit that would contain the interceptors and caching mechanism.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f289e8801c1d8a06ad95c6c6a42877a4/href">https://medium.com/media/f289e8801c1d8a06ad95c6c6a42877a4/href</a></iframe><p>With this, we can comfortably mark our request with the <strong>Cacheable </strong>annotation.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5c66e3069e3b11600f78e2d272efb614/href">https://medium.com/media/5c66e3069e3b11600f78e2d272efb614/href</a></iframe><p><strong>Unit Test</strong></p><p>To ensure that the above implementation works as expected, the unit test below helps to check if the <strong>Cacheable </strong>annotation is injected for specific requests.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/2ee5e639c361ba2176d2ec401c56a58f/href">https://medium.com/media/2ee5e639c361ba2176d2ec401c56a58f/href</a></iframe><h4><strong>Alternative Solution</strong></h4><p>Another solution to this challenge is to add a custom header to the request and then check if the added header is present in the interceptor.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6143a6962cf166960e02bb3137841bea/href">https://medium.com/media/6143a6962cf166960e02bb3137841bea/href</a></iframe><p>The offline interceptor would be:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/db216e2cff7e7ac61f03b39fc4a59217/href">https://medium.com/media/db216e2cff7e7ac61f03b39fc4a59217/href</a></iframe><h4>Conclusion</h4><p>In this article, we have seen how to use custom annotations to mark specific requests that require offline caching.</p><p>The concept presented in this article has been around for a while and it can be applied to different scenarios. Do well to explore other applications.</p><p>You can find the source code in the repository below.</p><p><a href="https://github.com/Ikhiloya/RetrofitAnnotationBasedCaching-Kotlin.git">Ikhiloya/RetrofitAnnotationBasedCaching-Kotlin</a></p><p>Thanks for reading and feel free to leave your comments and suggestions.</p><h3>References</h3><ol><li><a href="https://docs.oracle.com/javase/tutorial/java/annotations/">Oracle Java Annotations</a></li><li><a href="https://medium.com/kufar-tech/retrofit-tips-passing-custom-parameters-from-retrofits-request-to-okhttp-s-interceptor-989b8ceff07d">Retrofit tips: Passing custom parameters from Retrofit’s request to OkHttp’s interceptor</a> by <a href="https://medium.com/@rubikovitch?source=post_page-----989b8ceff07d--------------------------------">Ryben Ahanesian</a></li><li><a href="https://medium.com/@andretietz/custom-annotations-with-retrofit-2-8701ca7ce102">Custom Annotations with Retrofit 2</a> by <a href="https://medium.com/@andretietz?source=post_page-----8701ca7ce102--------------------------------">André Tietz</a></li><li><a href="https://stackoverflow.com/a/33791987/7962905">Good way to unit test Retrofit interface declaration.</a></li></ol><h4><strong>Resources on caching in android</strong></h4><ol><li><a href="https://medium.com/@bapspatil/caching-with-retrofit-store-responses-offline-71439ed32fda">Caching With Retrofit — Store Responses Offline</a> by <a href="https://medium.com/@bapspatil">Bapusaheb Patil</a></li><li><a href="https://www.journaldev.com/23297/android-retrofit-okhttp-offline-caching">Android Retrofit OkHttp Offline Caching</a></li><li><a href="https://www.youtube.com/watch?v=Orrh-Oiap58">Retrofit Caching Example and Why it SUCKS</a></li><li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control">Cache-Control</a></li></ol><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d7dbd775ac74" width="1" height="1" alt=""><hr><p><a href="https://medium.com/swlh/annotation-based-offline-caching-in-retrofit-d7dbd775ac74">Annotation-Based Offline Caching in Retrofit</a> was originally published in <a href="https://medium.com/swlh">The Startup</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Spring Boot with Firebase Storage]]></title>
            <link>https://medium.com/analytics-vidhya/spring-boot-with-firebase-storage-73f574af8c4?source=rss-1b0c24b58819------2</link>
            <guid isPermaLink="false">https://medium.com/p/73f574af8c4</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[strategy-design-pattern]]></category>
            <category><![CDATA[firebase-storage]]></category>
            <category><![CDATA[spring-boot]]></category>
            <category><![CDATA[heroku]]></category>
            <dc:creator><![CDATA[Ikhiloya Imokhai]]></dc:creator>
            <pubDate>Wed, 22 Jul 2020 10:13:47 GMT</pubDate>
            <atom:updated>2020-07-29T13:42:42.003Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6Se-sRhkGqo0hY_xMCVyAg.png" /></figure><p>Firebase provides a powerful, simple, and cost-effective object cloud storage service. Also, the Firebase SDK adds Google security to file uploads and downloads. The SDK can be used to store images, files, videos, and other user-generated content.</p><p>This article seeks to achieve the following:</p><ul><li>Leverage Firebase storage to upload and download files in an existing spring boot server deployed on Heroku.</li><li>Externalize Firebase service account configuration.</li></ul><p>In my previous <a href="https://blog.usejournal.com/practical-application-of-the-strategy-design-pattern-dbf50b2952ce">article</a>, the Strategy design pattern was used to develop a spring-boot application that uploads and downloads files using either local file storage or AWS storage implementation depending on the specified profile(development or production).</p><p>The base code contained in this <a href="https://github.com/Ikhiloya/spring-boot-strategy-design-pattern-">repository</a> has a file storage contract for upload and download of files represented by an abstract class.</p><p>This article will show how to integrate Firebase storage with the existing <a href="https://github.com/Ikhiloya/spring-boot-strategy-design-pattern-">spring boot application</a>.</p><h4>The Implementation</h4><p>The implementation of Firebase storage involves the following steps:</p><ol><li>Setup a Firebase project.</li><li>Add Firebase Admin SDK to the server.</li><li>Add Firebase Storage strategy implementation.</li></ol><blockquote>Let’s dive into the details of each step…</blockquote><ol><li>Setup Firebase project</li></ol><p>To use Firebase, you need to create a Firebase project from the Firebase developers console which you can find <a href="https://console.firebase.google.com/">here</a>. Follow the project setup wizard to create a new project.</p><p>Once the project is created, navigate to the project settings on the firebase console, and on the service account tab, click “generate new private key” to generate a new private key that is contained in a configuration JSON file.</p><p>The configuration file is a JSON file that contains security keys with which the server authenticates with Firebase storage programmatically via the unified Admin SDK.</p><p>2. Add Firebase Admin SDK</p><p>The Firebase Admin SDK is used to interact with Firebase from a privileged environment to perform realtime file upload and download amongst other functions as contained in the <a href="https://firebase.google.com/docs/admin/setup">documentation</a>.</p><p>Furthermore, to use Firebase admin SDK, the following is needed:</p><ul><li>A Firebase project, which you have created in step 1.</li><li>A configuration file with the service account credentials which you have obtained in step 1.</li></ul><p>Proceed to add the Firebase admin SDK maven dependency to the spring-boot server:</p><pre>&lt;dependency&gt;<br>  &lt;groupId&gt;com.google.firebase&lt;/groupId&gt;<br>  &lt;artifactId&gt;firebase-admin&lt;/artifactId&gt;<br>  &lt;version&gt;6.14.0&lt;/version&gt;<br>&lt;/dependency&gt;</pre><p>The next step is to initialize the Admin SDK in the application that lets you use Firebase storage.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1dd680026c407e97c8bf161c58531867/href">https://medium.com/media/1dd680026c407e97c8bf161c58531867/href</a></iframe><blockquote>Note that the bucket name and project id can be gotten from the configuration file.</blockquote><p>From the above, note that the file path to the service account configuration file was added. This is certainly not the best way to go as you do not want the file committed to source control. This will be handled in a later section.</p><p>3. Add Firebase Storage Strategy</p><p>Create a class that extends the existing storage strategy and provide firebase storage implementation for upload and download.</p><p>The upload method accepts a multipart file, creates a BlobInfo object before uploading the file to Firebase storage as can be seen below.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/485a822cf508bd570d397a1e7c0fe326/href">https://medium.com/media/485a822cf508bd570d397a1e7c0fe326/href</a></iframe><p>To download a file, pass the file name of an uploaded file to the firebase Storage service which returns a Blob object once the file is found.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/205665871acd0b292375b56de4ff3fce/href">https://medium.com/media/205665871acd0b292375b56de4ff3fce/href</a></iframe><p>The FirebaseStorageStrategy class can be seen below.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/82570b751441dc52d8d8774c367e281c/href">https://medium.com/media/82570b751441dc52d8d8774c367e281c/href</a></iframe><h4>Deploying to Heroku</h4><p>Deploying the application to Heroku will fail because Heroku does not support the file system as such the service account configuration file cannot be used. Also, you wouldn’t want to commit the configuration file to source control.</p><blockquote>How can this be fixed?</blockquote><h4>Proposed Solution</h4><p>The following steps can be taken to initialize the SDK in code without reading from a file.</p><ol><li>Create environment variables to hold the values contained in the service account configuration file. The same environment variable should be created on Heroku as part of the config vars.</li></ol><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3ea15d94a2dfd8feeebc463227ebad8c/href">https://medium.com/media/3ea15d94a2dfd8feeebc463227ebad8c/href</a></iframe><p>From the JSON structure above, create environment variables to hold the values.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ff3067d093c1d593505f94dff2ce8adb/href">https://medium.com/media/ff3067d093c1d593505f94dff2ce8adb/href</a></iframe><p>2. Create a class to hold the contents of the service account configuration file.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/855cf9a0f6521d5ee4080f8a7ce2dfaf/href">https://medium.com/media/855cf9a0f6521d5ee4080f8a7ce2dfaf/href</a></iframe><p>3. Set the values of the environment variables from step 1 to the class fields from step 2.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0407305eae76702880fd46a87381f761/href">https://medium.com/media/0407305eae76702880fd46a87381f761/href</a></iframe><p>4. Serialize the created object from step 3 to a JSON string using Jackson.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3d60b063f729962c496deae6475967cb/href">https://medium.com/media/3d60b063f729962c496deae6475967cb/href</a></iframe><p>5. Convert the serialized JSON string to an input stream.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/28f5b7efdec22946f5ccb3132316d08d/href">https://medium.com/media/28f5b7efdec22946f5ccb3132316d08d/href</a></iframe><p>6. Finally, initialize the SDK with the input stream object from step 5.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4c3c4fa97cf4970a1704fcec3d332eb4/href">https://medium.com/media/4c3c4fa97cf4970a1704fcec3d332eb4/href</a></iframe><p>The modified FirebaseStorageStrategy class can be seen below.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d9ed0a0f96c57a0af9e907734176716a/href">https://medium.com/media/d9ed0a0f96c57a0af9e907734176716a/href</a></iframe><h4>Conclusion</h4><p>From the use case explained, it can be seen that it is quite easy to integrate Firebase storage with spring boot and deploy to Heroku with an externalized configuration file.</p><p>Many thanks to<a href="https://gist.github.com/FrankDupree"> Frank Akogun</a> of the Lagos Java User Group for his valuable insights.</p><p>You can find the source code in the repository below.</p><p><a href="https://github.com/Ikhiloya/spring-boot-strategy-design-pattern-">Ikhiloya/spring-boot-strategy-design-pattern-</a></p><p>Thanks for reading 😊</p><h4>More Resources</h4><ul><li><a href="https://medium.com/@devesu/how-to-secure-your-firebase-project-even-when-your-api-key-is-publicly-available-a462a2a58843">How to secure your Firebase project even when your API key is publicly available</a> by <a href="https://medium.com/@devesu">Devesu</a></li><li><a href="https://firebase.google.com/docs/admin/setup#java">Firebase Admin Setup</a></li><li><a href="https://firebase.google.com/docs/storage">Cloud Storage</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=73f574af8c4" width="1" height="1" alt=""><hr><p><a href="https://medium.com/analytics-vidhya/spring-boot-with-firebase-storage-73f574af8c4">Spring Boot with Firebase Storage</a> was originally published in <a href="https://medium.com/analytics-vidhya">Analytics Vidhya</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Extending Android WorkManager for Dependent Periodic Tasks]]></title>
            <link>https://medium.com/swlh/extending-android-workmanager-for-dependent-periodic-tasks-7b14ab440d0c?source=rss-1b0c24b58819------2</link>
            <guid isPermaLink="false">https://medium.com/p/7b14ab440d0c</guid>
            <category><![CDATA[android-architecture]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[android-jetpack]]></category>
            <category><![CDATA[workmanager]]></category>
            <dc:creator><![CDATA[Ikhiloya Imokhai]]></dc:creator>
            <pubDate>Fri, 29 May 2020 18:15:18 GMT</pubDate>
            <atom:updated>2020-05-30T19:49:40.931Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lFrjXHUMRa6sJEVmOmPeeg.png" /><figcaption>source: <a href="https://android-developers.googleblog.com/2019/03/android-jetpack-workmanager-stable.html">Android Developers Blog</a></figcaption></figure><p>The android work manager API runs <strong>deferrable</strong>, <strong>guaranteed</strong> background work when specified <strong>constraints</strong> are satisfied. The API also supports both asynchronous one-off and periodic tasks. For more about Android WorkManager, please read the following articles:</p><ul><li><a href="https://medium.com/androiddevelopers/introducing-workmanager-2083bcfc4712">Introducing WorkManager</a> by <a href="https://medium.com/@pmaggi?source=post_page-----2083bcfc4712----------------------">Pietro Maggi</a></li><li><a href="https://medium.com/androiddevelopers/workmanager-basics-beba51e94048">WorkManager Basics </a>by <a href="https://medium.com/@lylalyla?source=post_page-----beba51e94048----------------------">Lyla Fujiwara</a></li><li><a href="https://medium.com/swlh/periodic-tasks-with-android-workmanager-c901dd9ba7bc">Periodic Tasks with Android WorkManager </a>by <a href="https://medium.com/@ikhiloyaimokhai">Ikhiloya Imokhai</a></li></ul><p>A Periodic <a href="https://developer.android.com/reference/androidx/work/WorkRequest">WorkRequest</a> is used to perform recurring <strong>deferrable tasks</strong>. This executes periodically with the first execution happening immediately the given <a href="https://developer.android.com/reference/androidx/work/Constraints">Constraints</a> are satisfied. For example, you may want to periodically backup your data, download fresh content in your app, push local data to a remote server, or upload logs to a server when constraints such as network availability, battery, or storage are satisfied.</p><blockquote><em>A </em><strong><em>deferrable task</em></strong><em> is one that does not need to be executed immediately.</em></blockquote><p>The WorkManager API does not support chaining of <a href="https://developer.android.com/reference/androidx/work/PeriodicWorkRequest"><em>PeriodicWorkRequest</em></a><em> </em>which makes it quite difficult to maintain a particular order for related dependent tasks as is done for a <a href="https://developer.android.com/reference/androidx/work/OneTimeWorkRequest"><em>OneTimeWorkRequest</em></a><em>.</em></p><p>In this article, I will show with an example of how we can maintain some sort of <strong>order</strong> or <strong>chaining</strong> between <strong>dependent</strong> <strong>related</strong> tasks for periodic work requests.</p><h4>The Use Case</h4><p>Let us consider an app to be used for the collection of personal data from a community in some remote location. Due to the anticipated network constraint of the location, the app is expected to work offline and push the data to a remote server when there is sufficient network coverage.</p><p>The pushing or posting of local data to a remote server when there is sufficient network coverage passes for a deferrable task and as such a Periodic Work request can be used to perform such operation.</p><p>The app is to capture each person’s data as well as information about their beneficiaries. It follows that a person can have more than one beneficiary which is a typical one-to-many relationship as can be seen below:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mh8eFPwLULupcpb9acRM3Q.png" /><figcaption>Figure 1: Entity Relationship Diagram</figcaption></figure><p>From figure 1, a Beneficiary cannot exist without a Person, hence, the creation of a Beneficiary presupposes that a Person must have been created.</p><p>For the offline app, the local data source(Android Room in this case) would be the single source of truth. As such, the primary key identifier for data stored locally would drive every CRUD operation we perform on the data.</p><p>Furthermore, we can deduce the following from figure 1 for the Person entity:</p><ul><li>localId : This is the primary key for the “person” entity. It identifies the record in the local database.</li><li>id : This is the primary key for the record in the <strong>remote</strong> database. It is null until the record is pushed to the remote database after which it is updated locally with the record id value from the remote.</li><li>state : This is used to know whether or not the local data has been pushed to the remote. It is set to pending when the data is resident in the local storage after which it is updated to synced state once the data is pushed to the remote.</li></ul><p>The data class for Person is shown below:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e7da07cf1205cf422afa94c323f2a74a/href">https://medium.com/media/e7da07cf1205cf422afa94c323f2a74a/href</a></iframe><p>Similarly, we can deduce the following from figure 1 for the Beneficiary entity:</p><ul><li>localId : This is the primary key for the “beneficiary” entity. It identifies the record in the local database.</li><li>id : This primary key for the record in the remote database.</li><li>personLocalId : This represents the foreign key in the person-to-beneficiary relationship locally.</li><li>state : This is used to know whether or not the local data has been pushed to the remote. It could either be pending or synced</li></ul><p>The data class for Beneficiary is shown below:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/818372bf6e8fdddcbc0f8054c76cac77/href">https://medium.com/media/818372bf6e8fdddcbc0f8054c76cac77/href</a></iframe><h4>The Challenge</h4><p>Since we have resolved to use a Periodic WorkRequest, how then do we ensure some form of order for the parent-child relationship that exists between the <strong>Person</strong> and <strong>Beneficiary</strong> entities?</p><blockquote>That is, how do we ensure that the <strong>Person</strong>(parent) entity is pushed to the remote before the <strong>Beneficiary</strong>(child) entity is pushed.</blockquote><p>This is necessary because the remote backend API requires that a Beneficiary is created with the remote id of the Person entity.</p><p>The above presents a daunting challenge since periodic work requests cannot be chained. To solve this, we need to device a means to ensure that the order of precedence as explained in the preceding paragraph is followed.</p><h4>The Proposed Solution</h4><p>To solve this problem, we will take the following steps:</p><ol><li>Create an abstract <strong>Worker</strong> <strong>Contract</strong> class that extends <a href="https://developer.android.com/reference/androidx/work/Worker.html">Android Worker</a> and defines our custom rules.</li><li>Create worker classes for Person and Beneficiary that extends from the Worker Contract class defined in step 1.</li><li>Create Periodic work requests using the worker classes created in step 2.</li></ol><p>Let’s dive into the details of each step.</p><h4>Step 1: Create a Worker Contract</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xBDIEZ_QPWqVR1HYeEvKng.png" /><figcaption>Figure 2: Worker Contract that Extends from Android Worker</figcaption></figure><p>This is an abstract class that defines the rules for pushing data stored locally to the remote data source. The contract shows the various conditions that must be met before an entity can be pushed to the remote. The class extends <a href="https://developer.android.com/reference/androidx/work/Worker.html">Android Worker</a> and overrides the <a href="https://developer.android.com/reference/androidx/work/Worker#doWork()">doWork</a>() method wherein the rules and order of execution of the periodic tasks are defined as can be seen in the code snippet below. It also contains various methods as explained with the comments shown in the code snippet below:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3fc291c3dfdac65349cef8e6e538968a/href">https://medium.com/media/3fc291c3dfdac65349cef8e6e538968a/href</a></iframe><p>The WorkerContract is the “engine” of this solution, as it brings all the pieces together and ensures that the required order of precedence is adhered to strictly.</p><blockquote>Depending on your scenario, your contract rules might be different.</blockquote><h4>Step 2: Create Worker classes</h4><p>With the worker contract well defined, we need to create worker classes that provide a concrete implementation of the WorkerContract for both Person and Beneficiary entities.</p><p>The PersonWorker extends from the WorkerContract and provides the logic for pushing Person data to the remote server. The code snippet below explains the PersonWorker class.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/23132796a8eba04ec0def8abdee2adbd/href">https://medium.com/media/23132796a8eba04ec0def8abdee2adbd/href</a></iframe><p>As can be seen from the saveCallResult method, once the local data is pushed successfully, the local data state and the remote id are updated accordingly.</p><p>Similarly, the BeneficiaryWorker implementation is shown below. Here, the hasParentRelationship method is set to return true because it is the child entity. Once the hasParentRelationship method returns true , we need to check if the Person entity associated with the Beneficiary via the personLocalId field has been pushed to the remote. This is done via the checkParentStatus() method which returns a boolean value depending on the state of the parent entity. This is shown in the code snippet below:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e69a09c28edba94fb693f6653f721362/href">https://medium.com/media/e69a09c28edba94fb693f6653f721362/href</a></iframe><h4>Step 3: Create Periodic Work Requests</h4><p>In this step, we will create periodic work requests for each worker since we have successfully defined our rules via the WorkerContract and also created concrete implementations of the WorkerContract.</p><p>First, we need to define the network constraints as shown below. That is, we want to push the local data once there is network connection.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a469ccd7da59845375e359b08354f9c3/href">https://medium.com/media/a469ccd7da59845375e359b08354f9c3/href</a></iframe><p>The periodic work request for saving a person is shown below.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/18cb88f4b1c26133ace5b553b9721b86/href">https://medium.com/media/18cb88f4b1c26133ace5b553b9721b86/href</a></iframe><p>Similarly, the Beneficiary work request is shown below:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d9bfb492f048a7b1d19e0baed5b17cf8/href">https://medium.com/media/d9bfb492f048a7b1d19e0baed5b17cf8/href</a></iframe><p>From the work requests above, note that we used a unique periodic work request to ensure that only one PeriodicWorkRequest of a particular name can be active at a time. For our case, we only want one operation to be active at a time; if there is a pending one, we will let it run to completion, otherwise, we insert the new work.</p><h4>Result</h4><p>Essentially, the person and beneficiary workers would start at the same time and will be executed as soon as the defined constraints are met. However, the Beneficiary worker would not be able to push data to the remote data source until the related person entity has been pushed as specified by the entity-relationship and the Worker Contract. By this, we can achieve a sense of order and precedence in the execution of the periodic tasks.</p><blockquote>Note that the solution presented in this article is by no means a general one. It is only specific to this use case. However, it gives a general idea as to how problems of the kind may be solved.</blockquote><h4>Output</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/1*o75HfIvfJuyrozU2hCsL_w.gif" /></figure><p>You can find the source code for the android client and spring-boot backend in the repository below.</p><p><a href="https://github.com/Ikhiloya/WorkManager-Dependent-Periodic-Task.git">Ikhiloya/WorkManager-Dependent-Periodic-Task</a></p><p>Thanks for reading and feel free to leave your comments and suggestions.</p><h3>References</h3><ul><li><a href="https://medium.com/androiddevelopers/introducing-workmanager-2083bcfc4712">Introducing WorkManager</a> by <a href="https://medium.com/@pmaggi?source=post_page-----2083bcfc4712----------------------">Pietro Maggi</a></li><li><a href="https://developer.android.com/reference/androidx/work/PeriodicWorkRequest">Periodic Work Request</a></li><li><a href="https://medium.com/androiddevelopers/workmanager-basics-beba51e94048">WorkManager Basics </a>by <a href="https://medium.com/@lylalyla?source=post_page-----beba51e94048----------------------">Lyla Fujiwara</a></li><li><a href="https://developer.android.com/topic/libraries/architecture/workmanager">Schedule tasks with WorkManager</a></li><li><a href="https://developer.android.com/topic/libraries/architecture/workmanager/basics">Getting started with WorkManager</a></li><li><a href="https://codelabs.developers.google.com/codelabs/android-workmanager">Background work with WorkManager codelab</a></li><li><a href="https://developer.android.com/topic/libraries/architecture/workmanager/how-to/recurring-work">Recurring Work</a></li><li><a href="https://medium.com/swlh/periodic-tasks-with-android-workmanager-c901dd9ba7bc">Periodic Tasks with Android WorkManager </a>by <a href="https://medium.com/@ikhiloyaimokhai">Ikhiloya Imokhai</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7b14ab440d0c" width="1" height="1" alt=""><hr><p><a href="https://medium.com/swlh/extending-android-workmanager-for-dependent-periodic-tasks-7b14ab440d0c">Extending Android WorkManager for Dependent Periodic Tasks</a> was originally published in <a href="https://medium.com/swlh">The Startup</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Practical Application of The Strategy Design Pattern]]></title>
            <link>https://ikhiloyaimokhai.medium.com/practical-application-of-the-strategy-design-pattern-dbf50b2952ce?source=rss-1b0c24b58819------2</link>
            <guid isPermaLink="false">https://medium.com/p/dbf50b2952ce</guid>
            <category><![CDATA[aws-storage]]></category>
            <category><![CDATA[sendgrid]]></category>
            <category><![CDATA[java-design-pattern]]></category>
            <category><![CDATA[strategy-design-pattern]]></category>
            <category><![CDATA[spring-boot]]></category>
            <dc:creator><![CDATA[Ikhiloya Imokhai]]></dc:creator>
            <pubDate>Thu, 14 May 2020 05:39:05 GMT</pubDate>
            <atom:updated>2020-05-20T23:43:48.604Z</atom:updated>
            <content:encoded><![CDATA[<h3>Application of The Strategy Design Pattern</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*TlGepX0Ue6o8POTxxZ9c6A.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/@hpzworkz?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Hassan Pasha</a> on <a href="https://unsplash.com/s/photos/chess-strategy?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><p>The Strategy design pattern is a behavioral software design pattern that defines a family of algorithms, encapsulates each one, and makes them interchangeable[1]. It enables the selection of the clients at runtime. The Strategy pattern is flexible since it defers the decision about which algorithm to use until runtime.</p><p>To find out more about the Strategy design pattern and the ideal scenarios for use, please see the reference section.</p><p>In this post, I’ll explain how I leveraged the strategy design pattern to solve my challenge.</p><h4>Problem statement</h4><p>To be able to handle multiple email clients and storage types depending on the environment or profile in which the application is running.<br>My project requires that Gmail client and local file storage be used for sending mails and storing files respectively while working locally[<em>dev-profile</em>].</p><p>Also, I needed to use the SendGrid mail client and AWS storage service for sending emails and storing files respectively while in production[<em>prod-profile</em>].</p><p>In summary, here is what I want to achieve:<br> <strong>dev profile</strong>: Gmail client and local file storage should be used for sending emails and storing files. <br> <strong>prod profile</strong>: SendGrid client and AWS storage for sending mails and storing files.</p><blockquote>This implementation is done using the Spring boot framework and leverages heavily on the inversion of control and dependency injection it provides.</blockquote><p>We’ll explore these applications independently.</p><blockquote><em>Let’s dive in…..</em></blockquote><ol><li><strong>Email Implementation.</strong></li></ol><p>The diagram below shows the high-level design of the Strategy pattern for our email service.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*TbGjTFu0BKNazkL3Bwk-Cw.png" /><figcaption>UML diagram for Mail Strategy</figcaption></figure><p>The strategy pattern ensures that the appropriate email client is used to send emails depending on your active profile: <strong>dev</strong> or <strong>prod</strong>. <br>The fundamental concept of the strategy pattern is to aggregate a family of related entities such that the application knows the appropriate client to use at runtime. This eliminates the need to write boilerplate code to ensure that the right client is used.</p><p>Let’s define the various classes as shown in the diagram above</p><ol><li>MailStrategy</li></ol><p>This is an interface that contains the various method definition for an email client. It defines the contract for all other concrete implementation. An abstract class could also suffice.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6ebda0327d8d35865de8b1578c7fde8e/href">https://medium.com/media/6ebda0327d8d35865de8b1578c7fde8e/href</a></iframe><p>2. SendGridEmailStrategy</p><p>This is a concrete implementation of the MailStrategy contract. It holds the logic for sending emails using the SendGrid client.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f1c1ef9df05bf665b942cae8bc6084a3/href">https://medium.com/media/f1c1ef9df05bf665b942cae8bc6084a3/href</a></iframe><p>3. GmailStrategy</p><p>This is a concrete implementation of the MailStrategy contract. It holds the logic for sending emails using the Gmail client.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/05ac8ca4550e1591ffb3aaaa744ac745/href">https://medium.com/media/05ac8ca4550e1591ffb3aaaa744ac745/href</a></iframe><p>4. MailFactory</p><p>To ensure that the appropriate email client is used at runtime depending on the specified active profile, a factory is used. This leverages the factory creation design pattern to create the right mail client.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/87fdfe87c8be1c8916e7ced84e9743ff/href">https://medium.com/media/87fdfe87c8be1c8916e7ced84e9743ff/href</a></iframe><p>5. MailService</p><p>A typical service class that holds the logic for sending emails. It is in the service layer that the beauty of the strategy pattern is seen. It does not need to worry about the specific implementation of the different mail strategies rather it just programs to the MailStrategy interface and lets the appropriate implementation be called at runtime.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d0886a556f55853cde0fd1e56e9f459c/href">https://medium.com/media/d0886a556f55853cde0fd1e56e9f459c/href</a></iframe><p>Remember the basic OOP principle:</p><blockquote>“program to interface, not implementations”</blockquote><p><strong>2. File Storage Implementation.</strong></p><p>The diagram below shows the high-level design of the storage strategy pattern implementation.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*SLiuE9SYP1a4msuCBP60ew.png" /><figcaption>UML diagram for Storage Strategy</figcaption></figure><p>Similarly, let’s define the various classes as shown in the diagram above.</p><ol><li>StorageStrategy</li></ol><p>This interface defines the contract for file storage. All other concrete implementations must abide by this contract. It contains just a method to upload a file as can be seen below:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/416d81e2f36a6c9d0450bc6af13bb493/href">https://medium.com/media/416d81e2f36a6c9d0450bc6af13bb493/href</a></iframe><p>2. AwsStorageStrategy</p><p>This provides the amazon web service(AWS) specific concrete implementation of file upload. It encapsulates the logic for uploading files to the AWS storage as can be seen below. This implementation is called only when the <strong>prod </strong>profile is specified during the build.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/eb77891190512c3bd8f95cb388bd03fb/href">https://medium.com/media/eb77891190512c3bd8f95cb388bd03fb/href</a></iframe><p>3. FileStorageStrategy</p><p>This is called when the <strong>dev </strong>profile specified. It holds the logic for storing files using the local file system as can be seen below. It stores files to a specified folder locally.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/625b40c39188a2f6192174b6bfa55d69/href">https://medium.com/media/625b40c39188a2f6192174b6bfa55d69/href</a></iframe><p>4. StorageFactory</p><p>This performs a similar function as the MailFactory already explained.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9109d4d06decb25fd74774bc2194ff5b/href">https://medium.com/media/9109d4d06decb25fd74774bc2194ff5b/href</a></iframe><p>5. StorageService</p><p>This is analogous to the MailService explained above. It is the service layer that holds the logic to upload files irrespective of the different implementations.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a7dd9ab77455c6b4c1a980c0579f09f7/href">https://medium.com/media/a7dd9ab77455c6b4c1a980c0579f09f7/href</a></iframe><p>Conclusion</p><p>As can be seen from the above implementations, it is very easy to switch between the dev and prod profiles without having to worry about the necessary implementation as it is taken care of at runtime owing to the strategy design pattern.</p><p>You could easily switch profile in spring by running this command:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/627c3c0672a275f8918ae83436fb3e62/href">https://medium.com/media/627c3c0672a275f8918ae83436fb3e62/href</a></iframe><p>Viola all the implementation code remains intact.</p><p>You can find the source code in the repository below.</p><p><a href="https://github.com/Ikhiloya/spring-boot-strategy-design-pattern-">Ikhiloya/spring-boot-strategy-design-pattern-</a></p><p>Thanks for reading and your suggestions and comments are welcomed.</p><h4>References</h4><ol><li>Head First Design Patterns</li></ol><p>📝 Save this story in <a href="https://usejournal.com/?utm_source=medium.com&amp;utm_medium=noteworthy_blog&amp;utm_campaign=tech&amp;utm_content=guest_post_read_later_text">Journal</a>.</p><p>👩‍💻 Wake up every Sunday morning to the week’s most noteworthy stories in Tech waiting in your inbox. <a href="https://usejournal.com/newsletter/noteworthy-in-tech/?utm_source=medium.com&amp;utm_medium=noteworthy_blog&amp;utm_campaign=tech&amp;utm_content=guest_post_text">Read the Noteworthy in Tech newsletter</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=dbf50b2952ce" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Spring boot JWT Authentication and authorization- An opinionated view]]></title>
            <link>https://ikhiloyaimokhai.medium.com/spring-boot-jwt-authentication-and-authorization-an-opinionated-view-70d595fcef42?source=rss-1b0c24b58819------2</link>
            <guid isPermaLink="false">https://medium.com/p/70d595fcef42</guid>
            <category><![CDATA[spring-boot]]></category>
            <category><![CDATA[opinion]]></category>
            <category><![CDATA[jwt-auth]]></category>
            <category><![CDATA[jwt-token]]></category>
            <category><![CDATA[project-review]]></category>
            <dc:creator><![CDATA[Ikhiloya Imokhai]]></dc:creator>
            <pubDate>Mon, 10 Feb 2020 08:48:19 GMT</pubDate>
            <atom:updated>2021-02-11T15:41:59.027Z</atom:updated>
            <content:encoded><![CDATA[<h3>Spring boot JWT Authentication and authorization- A Review</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DRoU-HtC9F6EVmfc7B04pA.png" /><figcaption>source: <a href="https://jwt.io/">JWT</a></figcaption></figure><p>Recently I started a small project which required authentication and authorization. As a Java developer familiar with spring boot, I decided to leverage on spring boot to deliver the goods.</p><p>One key decision I made early on was to use JWT for authentication and authorization as I have read about the stateless capacity of JWTs.</p><p>However, along the line, I encountered some issues and made some personal findings that form the core of this article.</p><blockquote><strong>Disclaimer</strong>: As the title suggests, the views expressed here are based on my experience with JWTs which is meant to elicit opinions and expand the discussion about JWTs and it’s inherent capacities.</blockquote><p>I will begin by explaining how I set up the authentication and authorization in spring boot as well as the modifications made to circumvent the issues faced.</p><p><em>Note this project uses a Postgres data source</em></p><p>The code below shows the initial JWT filter.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e354f36d6d6fd4c7a9bdd91c683e5aaa/href">https://medium.com/media/e354f36d6d6fd4c7a9bdd91c683e5aaa/href</a></iframe><h4>Challenges<br><strong>1. User Log out</strong></h4><p>One of the challenges I faced during this implementation was how to securely log out a user.</p><p><strong>The Scenario</strong></p><p>In this case, when a user logs out of the client app, the token is still valid if it’s not expired before logout. This poses a security threat as the valid token can be “hijacked” and used to access resources from an unknown client. <br>With this flaw, the whole idea of stateless authentication and authorization was defeated as there is no clear way of invalidating a token.</p><p><strong><em>Remediation</em></strong></p><p>To circumvent this issue, I had to make do with popular opinions and suggestions on this matter. Create an <strong>in-memory</strong> data source to store the token as part of <strong><em>blacklisted JWTs </em></strong>when the user logs out, authenticate against them for each request to the server and routinely delete expired tokens from the data source to reduce the sample size, hence improving search time for each request.</p><p>I achieved this seamlessly by checking if the token is already in the list of blacklisted tokens maintained. This is shown below although I used the Postgres database.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3a2eb843873dad3e60117e9697af6c7e/href">https://medium.com/media/3a2eb843873dad3e60117e9697af6c7e/href</a></iframe><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1c6e43c7b28a0b4d8e76fe19896d4cc3/href">https://medium.com/media/1c6e43c7b28a0b4d8e76fe19896d4cc3/href</a></iframe><p>No doubt this works fine, but could potentially increase the response time for every request as the JWT blacklist is queried for every request. This was a cost I had to make do with.</p><p><strong>2. Database Authentication not done for each request</strong></p><p>Another funny behaviour I noticed whilst moving from my test environment to the UAT environment was that while requesting a resource, database authentication is not done. That is, only the validity of the token is checked and once a token is valid, <em>voila! </em>a resource is granted to the client. This may not be the usual case as the secret key used in the generation of the token should be securely protected <em>but what if in extreme cases</em>, the secret key is exposed. The attacker can easily generate a signed token and would be able to access a resource on your server.</p><p><strong>The Scenario</strong></p><p>In my case, the token I used for the test was able to access resources from my UAT server although both used the same secret for the token generation which should not have been. The point I’m trying to make here is that for each request there’s no authentication against the database to ascertain the validity of the <strong><em>principal</em></strong> in the token.</p><p><strong>Remediation</strong></p><p>To circumvent this, I added another layer of check to the JWT filter to do a check against the database for every request to the server. This again added another complexity as it may potentially lead to a lag in the server owing to the routine authentication against the database.</p><p>See below the code for the above implementation.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/56b0c5d396fe82afe572e7781c615495/href">https://medium.com/media/56b0c5d396fe82afe572e7781c615495/href</a></iframe><p><strong>Questions</strong></p><p>Considering the aforementioned scenarios, I would like to pose the following questions to my audience and developers experienced using JWTs.</p><p>1. What is the ideal use case of JWTs?<br>2. What security measures apart from the aforementioned can be used to safely secure apps using JWTs without compromising the stateless nature of JWTs?</p><p><strong>Conclusion</strong> <br>Like I said ab initio, the opinions expressed herein are mine and based on the scenarios that played out whilst working with JWTs in spring boot.</p><p>Feel free to expand these issues so we can get a clearer perspective of the modus operandi of JWTs and potential pitfalls to avoid.</p><p>Thanks :)</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=70d595fcef42" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>