<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Manit Aggarwal]]></title><description><![CDATA[Manit Aggarwal]]></description><link>https://manitaggarwal.com</link><generator>RSS for Node</generator><lastBuildDate>Fri, 17 Apr 2026 09:48:46 GMT</lastBuildDate><atom:link href="https://manitaggarwal.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[RAG using Spring AI]]></title><description><![CDATA[Retrieval Augmented Generation (RAG) is a technique used to give LLMs access to specific data. In this post, we will build a RAG application using Spring AI and PostgreSQL.
What is RAG?
RAG combines the power of LLMs with specialized data. Instead of...]]></description><link>https://manitaggarwal.com/rag-using-spring-ai</link><guid isPermaLink="true">https://manitaggarwal.com/rag-using-spring-ai</guid><category><![CDATA[RAG ]]></category><category><![CDATA[Java]]></category><category><![CDATA[spring ai]]></category><dc:creator><![CDATA[Manit Aggarwal]]></dc:creator><pubDate>Wed, 28 Jan 2026 06:24:48 GMT</pubDate><content:encoded><![CDATA[<p>Retrieval Augmented Generation (RAG) is a technique used to give LLMs access to specific data. In this post, we will build a RAG application using Spring AI and PostgreSQL.</p>
<h2 id="heading-what-is-rag">What is RAG?</h2>
<p>RAG combines the power of LLMs with specialized data. Instead of training a model on your data, you store your documents in a vector database. When a user asks a question, the system retrieves relevant chunks of data and provides them to the LLM as context.</p>
<h2 id="heading-what-rag-looks-like-in-practice">What RAG Looks Like in Practice</h2>
<p>I didn’t want to fine-tune a model or manage embeddings manually. The goal was to:</p>
<ul>
<li><p>Upload documents (PDFs, text files, etc.)</p>
</li>
<li><p>Chunk and embed them automatically</p>
</li>
<li><p>Query them using metadata-based filtering</p>
</li>
<li><p>Let the LLM answer questions using retrieved context</p>
</li>
</ul>
<p>Spring AI handles does most of the heavy lifting, which keeps the codebase small and readable.</p>
<h2 id="heading-maven-dependencies">Maven Dependencies</h2>
<p>We need several Spring AI starters for OpenAI, PGVector, and document parsing.</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependencies</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.ai<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-ai-starter-model-openai<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.ai<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-ai-starter-vector-store-pgvector<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.ai<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-ai-tika-document-reader<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">dependencies</span>&gt;</span>
</code></pre>
<h2 id="heading-configuration">Configuration</h2>
<p>We configure the OpenAI model and PostgreSQL connection in <code>application.yaml</code>.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">spring:</span>
  <span class="hljs-attr">application:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">rag</span>
  <span class="hljs-attr">ai:</span>
    <span class="hljs-attr">openai:</span>
      <span class="hljs-attr">api-key:</span> <span class="hljs-string">${OPENAI_KEY}</span>
      <span class="hljs-attr">chat:</span>
        <span class="hljs-attr">options:</span>
          <span class="hljs-attr">model:</span> <span class="hljs-string">gpt-5-nano</span>
      <span class="hljs-attr">embedding:</span>
        <span class="hljs-attr">options:</span>
          <span class="hljs-attr">model:</span> <span class="hljs-string">text-embedding-3-small</span>
    <span class="hljs-attr">vectorstore:</span>
      <span class="hljs-attr">pgvector:</span>
        <span class="hljs-attr">table-name:</span> <span class="hljs-string">rag</span>
        <span class="hljs-attr">schema-name:</span> <span class="hljs-string">store</span>
        <span class="hljs-attr">initialize-schema:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">datasource:</span>
    <span class="hljs-attr">url:</span> <span class="hljs-string">${POSTGRES_URL}</span>
    <span class="hljs-attr">username:</span> <span class="hljs-string">${POSTGRES_USERNAME}</span>
    <span class="hljs-attr">password:</span> <span class="hljs-string">${POSTGRES_PASSWORD}</span>
  <span class="hljs-attr">servlet:</span>
    <span class="hljs-attr">multipart:</span>
      <span class="hljs-attr">max-file-size:</span> <span class="hljs-string">50MB</span>
</code></pre>
<h2 id="heading-document-upload">Document Upload</h2>
<p>The <code>UploadController</code> handles reading files using Apache Tika, splitting them into tokens, and storing them in the vector store.</p>
<pre><code class="lang-java"><span class="hljs-meta">@PostMapping("/file")</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> ResponseEntity&lt;String&gt; <span class="hljs-title">uploadFile</span><span class="hljs-params">(<span class="hljs-meta">@RequestParam("file")</span> MultipartFile file)</span> </span>{
    <span class="hljs-keyword">try</span> {
        TikaDocumentReader documentReader = <span class="hljs-keyword">new</span> TikaDocumentReader(toResource(file));
        TextSplitter textSplitter = <span class="hljs-keyword">new</span> TokenTextSplitter();
        vectorStore.add(textSplitter.split(documentReader.read()));
        <span class="hljs-keyword">return</span> ResponseEntity.ok(<span class="hljs-string">"file successfully uploaded and processed"</span>);
    } <span class="hljs-keyword">catch</span> (IOException e) {
        <span class="hljs-keyword">return</span> ResponseEntity.status(<span class="hljs-number">500</span>).body(<span class="hljs-string">"Error processing file: "</span> + e.getMessage());
    } <span class="hljs-keyword">catch</span> (Exception e) {
        <span class="hljs-keyword">return</span> ResponseEntity.status(<span class="hljs-number">500</span>).body(<span class="hljs-string">"Error: "</span> + e.getMessage());
    }
}

<span class="hljs-function"><span class="hljs-keyword">public</span> Resource <span class="hljs-title">toResource</span><span class="hljs-params">(MultipartFile file)</span> <span class="hljs-keyword">throws</span> IOException </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> InputStreamResource(file.getInputStream()) {
        <span class="hljs-meta">@Override</span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getFilename</span><span class="hljs-params">()</span> </span>{
            <span class="hljs-keyword">return</span> file.getOriginalFilename();
        }

        <span class="hljs-meta">@Override</span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">long</span> <span class="hljs-title">contentLength</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> IOException </span>{
            <span class="hljs-keyword">return</span> file.getSize();
        }
    };
}
</code></pre>
<h2 id="heading-chat-with-context">Chat with Context</h2>
<p>The <code>QuestionController</code> retrieves relevant documents from PostgreSQL using <code>QuestionAnswerAdvisor</code> and generates a response from the LLM.</p>
<p>SearchRequest helps in filtering out the documents based on different filenames that were embedded, which is notebook param in the RequestParm.</p>
<pre><code class="lang-java"><span class="hljs-meta">@GetMapping("/rag/chat")</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">chat</span><span class="hljs-params">(<span class="hljs-meta">@RequestParam(value = "prompt", defaultValue = "Tell me a joke.")</span> String prompt,
                   <span class="hljs-meta">@RequestParam(value = "notebook", defaultValue = "embed.txt")</span> String notebook)</span> </span>{
    <span class="hljs-keyword">long</span> startTime = System.currentTimeMillis();
    SearchRequest searchRequest = SearchRequest.builder()
            .filterExpression(String.format(<span class="hljs-string">"source == '%s'"</span>, notebook))
            .build();
    ChatResponse chatResponse = <span class="hljs-keyword">this</span>.chatClient.prompt()
            .user(prompt)
            .advisors(QuestionAnswerAdvisor
                    .builder(vectorStore)
                    .searchRequest(searchRequest)
                    .build())
            .call()
            .chatResponse();
    <span class="hljs-keyword">assert</span> chatResponse != <span class="hljs-keyword">null</span>;
    Usage usage = chatResponse.getMetadata().getUsage();
    <span class="hljs-keyword">int</span> promptTokens = usage.getPromptTokens();
    <span class="hljs-keyword">int</span> completionTokens = usage.getCompletionTokens();
    <span class="hljs-keyword">long</span> endTime = System.currentTimeMillis();
    <span class="hljs-keyword">long</span> responseTime = endTime - startTime;
    <span class="hljs-keyword">return</span> String.format(<span class="hljs-string">""</span><span class="hljs-string">"
                    Response from LLM is\s

                    %s

                    Prompt tokens used %s
                    Completion tokens used %s
                    Response time %s ms"</span><span class="hljs-string">""</span>,
            chatResponse.getResult().getOutput().getText(),
            promptTokens, completionTokens, responseTime);
}
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Spring AI makes it easy to integrate vector stores and LLMs into a Java application. By using <code>PgVectorStore</code> and <code>QuestionAnswerAdvisor</code>, we can implement a full RAG pipeline with very little code.</p>
]]></content:encoded></item><item><title><![CDATA[Building a Memory Lane with a Spring Boot MCP Server]]></title><description><![CDATA[This blog post will guide you through the process of creating an MCP server in Spring Boot. We'll use a practical example of a JournalTool that allows an AI to create and retrieve journal entries.
What is an MCP Server?
Imagine an AI assistant that c...]]></description><link>https://manitaggarwal.com/building-a-memory-lane-with-a-spring-boot-mcp-server</link><guid isPermaLink="true">https://manitaggarwal.com/building-a-memory-lane-with-a-spring-boot-mcp-server</guid><category><![CDATA[Java]]></category><category><![CDATA[spring ai]]></category><category><![CDATA[Model Context Protocol]]></category><dc:creator><![CDATA[Manit Aggarwal]]></dc:creator><pubDate>Mon, 09 Jun 2025 15:53:03 GMT</pubDate><content:encoded><![CDATA[<p>This blog post will guide you through the process of creating an MCP server in Spring Boot. We'll use a practical example of a <code>JournalTool</code> that allows an AI to create and retrieve journal entries.</p>
<h3 id="heading-what-is-an-mcp-server">What is an MCP Server?</h3>
<p>Imagine an AI assistant that can't access your calendar, send an email, or look up information on the internet. Its usefulness would be severely limited. An MCP server acts as a bridge between your AI model and the outside world. It exposes a set of tools—essentially, functionalities that the AI can call upon to perform specific tasks. This could be anything from fetching data from a database, calling a REST API, or, in our case, managing a journal.</p>
<p>Spring AI provides excellent support for building MCP servers, making it surprisingly simple to expose your application's capabilities to an AI.</p>
<h3 id="heading-key-concepts-in-our-mcp-server">Key Concepts in Our MCP Server</h3>
<p>Let's break down the important annotations and components in our <code>JournalTool</code>:</p>
<ul>
<li><p><code>@Service</code>: This is a standard Spring annotation that marks the class as a service bean, making it a candidate for dependency injection and part of the application's business logic.</p>
</li>
<li><p><code>@Tool</code>: By annotating a method with <code>@Tool</code>, you are exposing it as a callable function to the AI model.</p>
<ul>
<li><p><code>name</code>: This attribute provides a specific name for the tool. The AI will use this name to refer to the function.</p>
</li>
<li><p><code>description</code>: This is crucial. The description provides the AI with the necessary context to understand what the tool does, what its parameters are for, and when to use it. A well-written description is key to the AI's ability to effectively use your tool.</p>
</li>
</ul>
</li>
</ul>
<pre><code class="lang-java"><span class="hljs-meta">@Service</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">JournalTool</span> </span>{

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> JournalRepository journalRepository;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">JournalTool</span><span class="hljs-params">(JournalRepository journalRepository)</span> </span>{
        <span class="hljs-keyword">this</span>.journalRepository = journalRepository;
    }

    <span class="hljs-meta">@Tool(name = "createJournalEntry",
            description = "Creates a new journal entry with the given journal text, type, and hashtag." +
                    " Use this when no date is given for the journal entry. Also, use the most" +
                    " important words from the journal in hashtag, seperated by a -")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">createJournalEntry</span><span class="hljs-params">(String journal, String journalType, String hashtag)</span> </span>{
        <span class="hljs-keyword">try</span> {
            <span class="hljs-comment">// Create a new journal entry</span>
            <span class="hljs-keyword">var</span> journalEntry = <span class="hljs-keyword">new</span> com.manitaggarwal.memory.entity.JournalEntry();
            journalEntry.setJournal(journal);
            journalEntry.setJournalType(journalType);
            journalEntry.setHashtag(hashtag);
            journalEntry.setJournalDateTime(<span class="hljs-keyword">new</span> java.util.Date());

            <span class="hljs-comment">// Save the journal entry to the repository</span>
            journalRepository.save(journalEntry);

            <span class="hljs-keyword">return</span> <span class="hljs-string">"Journal entry created successfully"</span>;
        } <span class="hljs-keyword">catch</span> (Exception e) {
            <span class="hljs-keyword">return</span> <span class="hljs-string">"Error creating journal entry: "</span> + e.getMessage();
        }
    }

    <span class="hljs-meta">@Tool(name = "createJournalEntryForParticularDate",
            description = "Creates a new journal entry with the given journal text, type, and hashtag." +
                    " Use this when a date is given for the journal entry. Also, use the most" +
                    " important words from the journal in hashtag, seperated by a -")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">createJournalEntryForSomeday</span><span class="hljs-params">(String journal, String journalType, String hashtag,
                                               Date date)</span> </span>{
        <span class="hljs-keyword">try</span> {
            <span class="hljs-comment">// Create a new journal entry</span>
            <span class="hljs-keyword">var</span> journalEntry = <span class="hljs-keyword">new</span> com.manitaggarwal.memory.entity.JournalEntry();
            journalEntry.setJournal(journal);
            journalEntry.setJournalType(journalType);
            journalEntry.setHashtag(hashtag);
            journalEntry.setJournalDateTime(date);

            <span class="hljs-comment">// Save the journal entry to the repository</span>
            journalRepository.save(journalEntry);

            <span class="hljs-keyword">return</span> <span class="hljs-string">"Journal entry created successfully"</span>;
        } <span class="hljs-keyword">catch</span> (Exception e) {
            <span class="hljs-keyword">return</span> <span class="hljs-string">"Error creating journal entry: "</span> + e.getMessage();
        }
    }

    <span class="hljs-meta">@Tool(name = "get_journal_entry_by_date",
            description = "Retrieves journal entries for a specific date.")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getJournalEntryByDate</span><span class="hljs-params">(Date date)</span> </span>{
        <span class="hljs-keyword">try</span> {
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(date);

            <span class="hljs-comment">// Set to start of day</span>
            calendar.set(Calendar.HOUR_OF_DAY, <span class="hljs-number">0</span>);
            calendar.set(Calendar.MINUTE, <span class="hljs-number">0</span>);
            calendar.set(Calendar.SECOND, <span class="hljs-number">0</span>);
            calendar.set(Calendar.MILLISECOND, <span class="hljs-number">0</span>);
            Date startOfDay = calendar.getTime();

            <span class="hljs-comment">// Set to start of next day</span>
            calendar.add(Calendar.DATE, <span class="hljs-number">1</span>);
            Date startOfNextDay = calendar.getTime();
            <span class="hljs-comment">// Retrieve journal entries by date</span>
            <span class="hljs-keyword">var</span> journalEntries = journalRepository.findAllByJournalDateTimeBetween(startOfDay,
                    startOfNextDay);
            <span class="hljs-keyword">if</span> (journalEntries.isEmpty()) {
                <span class="hljs-keyword">return</span> <span class="hljs-string">"No journal entries found for the specified date."</span>;
            }
            StringBuilder response = <span class="hljs-keyword">new</span> StringBuilder(<span class="hljs-string">"Journal entries for "</span> + date + <span class="hljs-string">":\n"</span>);
            <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> entry : journalEntries) {
                response.append(<span class="hljs-string">"Serial Number: "</span>).append(entry.getSerialNumber())
                        .append(<span class="hljs-string">", Journal: "</span>).append(entry.getJournal())
                        .append(<span class="hljs-string">", Type: "</span>).append(entry.getJournalType())
                        .append(<span class="hljs-string">", Hashtag: "</span>).append(entry.getHashtag())
                        .append(<span class="hljs-string">", Date: "</span>).append(entry.getJournalDateTime())
                        .append(<span class="hljs-string">"\n"</span>);
            }
            <span class="hljs-keyword">return</span> response.toString();
        } <span class="hljs-keyword">catch</span> (Exception e) {
            <span class="hljs-keyword">return</span> <span class="hljs-string">"Error retrieving journal entries: "</span> + e.getMessage();
        }
    }

    <span class="hljs-meta">@Tool(name = "get_journal_entry_between_date",
            description = "Retrieves journal entries between two specific dates.")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getJournalEntryByDate</span><span class="hljs-params">(Date startDate, Date endDate)</span> </span>{
        <span class="hljs-keyword">try</span> {
            <span class="hljs-keyword">var</span> journalEntries = journalRepository.findAllByJournalDateTimeBetween(startDate,
                    endDate);
            <span class="hljs-keyword">if</span> (journalEntries.isEmpty()) {
                <span class="hljs-keyword">return</span> <span class="hljs-string">"No journal entries found for the specified date."</span>;
            }
            StringBuilder response = <span class="hljs-keyword">new</span> StringBuilder(<span class="hljs-string">"Journal entries between "</span> + startDate + <span class="hljs-string">":\n"</span>
                    + <span class="hljs-string">" and "</span> + endDate + <span class="hljs-string">":\n"</span>);
            <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> entry : journalEntries) {
                response.append(<span class="hljs-string">"Serial Number: "</span>).append(entry.getSerialNumber())
                        .append(<span class="hljs-string">", Journal: "</span>).append(entry.getJournal())
                        .append(<span class="hljs-string">", Type: "</span>).append(entry.getJournalType())
                        .append(<span class="hljs-string">", Hashtag: "</span>).append(entry.getHashtag())
                        .append(<span class="hljs-string">", Date: "</span>).append(entry.getJournalDateTime())
                        .append(<span class="hljs-string">"\n"</span>);
            }
            <span class="hljs-keyword">return</span> response.toString();
        } <span class="hljs-keyword">catch</span> (Exception e) {
            <span class="hljs-keyword">return</span> <span class="hljs-string">"Error retrieving journal entries: "</span> + e.getMessage();
        }
    }

    <span class="hljs-meta">@Tool(name = "get_journal_entry_by_search_params",
            description = "Retrieves journal entries based on search parameters in hashtags.")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getJournalEntryByHashtag</span><span class="hljs-params">(List&lt;String&gt; searchParams)</span> </span>{
        <span class="hljs-keyword">try</span> {
            StringBuilder response = <span class="hljs-keyword">new</span> StringBuilder(<span class="hljs-string">"Journal entries matching search parameters:\n"</span>);
            <span class="hljs-keyword">for</span> (String param : searchParams) {
                <span class="hljs-keyword">var</span> journalEntries = journalRepository.findAllByHashtagContaining(param);
                <span class="hljs-keyword">if</span> (journalEntries.isEmpty()) {
                    response.append(<span class="hljs-string">"No journal entries found for hashtag: "</span>).append(param).append(<span class="hljs-string">"\n"</span>);
                } <span class="hljs-keyword">else</span> {
                    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> entry : journalEntries) {
                        response.append(<span class="hljs-string">"Serial Number: "</span>).append(entry.getSerialNumber())
                                .append(<span class="hljs-string">", Journal: "</span>).append(entry.getJournal())
                                .append(<span class="hljs-string">", Type: "</span>).append(entry.getJournalType())
                                .append(<span class="hljs-string">", Hashtag: "</span>).append(entry.getHashtag())
                                .append(<span class="hljs-string">", Date: "</span>).append(entry.getJournalDateTime())
                                .append(<span class="hljs-string">"\n"</span>);
                    }
                }
            }
            <span class="hljs-keyword">return</span> response.toString();
        } <span class="hljs-keyword">catch</span> (Exception e) {
            <span class="hljs-keyword">return</span> <span class="hljs-string">"Error retrieving journal entries: "</span> + e.getMessage();
        }
    }
}
</code></pre>
<p>The Model Context Protocol and Spring AI are democratizing the ability to build powerful, context-aware AI applications. By creating a simple yet effective MCP server, you can unlock a new level of interaction and intelligence for your AI models. The <code>JournalTool</code> example is just the beginning.</p>
<p>Git repository link can be found <a target="_blank" href="https://github.com/manitaggarwal/mcp-journal">here</a>.</p>
]]></content:encoded></item><item><title><![CDATA[How to Automate Backups with Go]]></title><description><![CDATA[In today's digital age, data loss can be catastrophic for individuals and businesses alike. Having a reliable backup system in place is crucial to ensure the safety and availability of your important files and documents. Let's discuss how to automate...]]></description><link>https://manitaggarwal.com/how-to-automate-backups-with-go</link><guid isPermaLink="true">https://manitaggarwal.com/how-to-automate-backups-with-go</guid><category><![CDATA[Go Language]]></category><category><![CDATA[Backup]]></category><category><![CDATA[learning]]></category><dc:creator><![CDATA[Manit Aggarwal]]></dc:creator><pubDate>Fri, 26 May 2023 06:39:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/fd9mIBluHkA/upload/8cdd7a2388c5d2069e08b4e496af58cf.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In today's digital age, data loss can be catastrophic for individuals and businesses alike. Having a reliable backup system in place is crucial to ensure the safety and availability of your important files and documents. Let's discuss how to automate backups using the Go programming language.</p>
<h3 id="heading-prerequisites"><strong>Prerequisites</strong></h3>
<p>Before we dive into the backup script, let's make sure we have the necessary prerequisites set up:</p>
<ol>
<li><p>Go Installation: Make sure you have Go installed on your machine. You can download it from the official Go website (<a target="_blank" href="https://golang.org"><strong>https://golang.org</strong></a>) and follow the installation instructions for your operating system.</p>
</li>
<li><p>External Dependencies: Ensure that the <code>tar</code> and <code>scp</code> (if using <code>scp</code> backup) or <code>rclone</code> (if using <code>rclone</code> backup) commands are available on your system. These commands are used by the backup script to create the backup archive and copy it to the destination server.</p>
</li>
</ol>
<h3 id="heading-getting-started"><strong>Getting Started</strong></h3>
<p>Now that we have the prerequisites in place, let's start writing the backup script in Go. Open your favorite text editor or IDE and create a new file called <code>main.go</code>. Copy the following code into the file:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"log"</span>
    <span class="hljs-string">"os"</span>
    <span class="hljs-string">"os/exec"</span>
    <span class="hljs-string">"time"</span>

    <span class="hljs-string">"github.com/spf13/viper"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {

    <span class="hljs-comment">// read values from configuration file</span>
    configPath := os.Getenv(<span class="hljs-string">"BACKUP_CONFIG_PATH"</span>)
    <span class="hljs-keyword">if</span> configPath == <span class="hljs-string">""</span> {
        log.Fatalf(<span class="hljs-string">"missing BACKUP_CONFIG_PATH environment variable"</span>)
        os.Exit(<span class="hljs-number">1</span>)
    }
    viper.SetConfigFile(configPath)
    <span class="hljs-keyword">if</span> err := viper.ReadInConfig(); err != <span class="hljs-literal">nil</span> {
        fmt.Printf(<span class="hljs-string">"Failed to read configuration file: %v\n"</span>, err)
        os.Exit(<span class="hljs-number">1</span>)
    }

    <span class="hljs-comment">// replace these values with your own directory path and server details</span>
    dirPath := viper.GetString(<span class="hljs-string">"dirPath"</span>)
    server := viper.GetString(<span class="hljs-string">"server"</span>)
    backupType := viper.GetString(<span class="hljs-string">"backupType"</span>)

    archiveFileName := viper.GetString(<span class="hljs-string">"archiveFileName"</span>) + <span class="hljs-string">"_"</span> +
        time.Now().Format(<span class="hljs-string">"2006-01-02_15-04"</span>) + <span class="hljs-string">".tar.gz"</span>

    <span class="hljs-comment">// create a tar archive of the directory</span>
    tarCmd := exec.Command(<span class="hljs-string">"tar"</span>, <span class="hljs-string">"-czf"</span>, archiveFileName, dirPath)
    <span class="hljs-keyword">if</span> err := tarCmd.Run(); err != <span class="hljs-literal">nil</span> {
        fmt.Printf(<span class="hljs-string">"Failed to create tar archive: %v\n"</span>, err)
        os.Exit(<span class="hljs-number">1</span>)
    }

    <span class="hljs-keyword">if</span> backupType == <span class="hljs-string">"scp"</span> {
        <span class="hljs-comment">// copy the archive to the server via scp</span>
        <span class="hljs-keyword">if</span> err := copyArchiveToServer(server, archiveFileName); err != <span class="hljs-literal">nil</span> {
            fmt.Printf(<span class="hljs-string">"Failed to copy archive to server: %v\n"</span>, err)
            removeArchive(archiveFileName)
            os.Exit(<span class="hljs-number">1</span>)
        }
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> backupType == <span class="hljs-string">"rclone"</span> {
        <span class="hljs-comment">// copy the archive to the new server via rclone</span>
        rcloneCmd := exec.Command(<span class="hljs-string">"rclone"</span>, <span class="hljs-string">"copy"</span>, archiveFileName, server)
        <span class="hljs-keyword">if</span> err := rcloneCmd.Run(); err != <span class="hljs-literal">nil</span> {
            fmt.Printf(<span class="hljs-string">"Failed to copy archive to object storage: %v\n"</span>, err)
            removeArchive(archiveFileName)
            os.Exit(<span class="hljs-number">1</span>)
        }
    } <span class="hljs-keyword">else</span> {
        fmt.Printf(<span class="hljs-string">"Invalid backup type: %v\n, can be scp or rclone"</span>, backupType)
        removeArchive(archiveFileName)
        os.Exit(<span class="hljs-number">1</span>)
    }

    <span class="hljs-comment">// delete the local archive file</span>
    <span class="hljs-keyword">if</span> err := removeArchive(archiveFileName); err != <span class="hljs-literal">nil</span> {
        fmt.Printf(<span class="hljs-string">"Failed to delete local archive file: %v\n"</span>, err)
        os.Exit(<span class="hljs-number">1</span>)
    }

    fmt.Println(<span class="hljs-string">"Archive created and copied to new server successfully."</span>)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">copyArchiveToServer</span><span class="hljs-params">(server <span class="hljs-keyword">string</span>, file <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">error</span></span> {
    scpCmd := exec.Command(<span class="hljs-string">"scp"</span>, file, server)
    <span class="hljs-keyword">if</span> err := scpCmd.Run(); err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> fmt.Errorf(<span class="hljs-string">"failed to copy archive to new server: %v"</span>, err)
    }
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">removeArchive</span><span class="hljs-params">(file <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">error</span></span> {
    <span class="hljs-keyword">if</span> err := os.Remove(file); err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> fmt.Errorf(<span class="hljs-string">"failed to delete %v: %v"</span>, file, err)
    }
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}
</code></pre>
<h3 id="heading-understanding-the-script"><strong>Understanding the Script</strong></h3>
<p>Let's take a moment to understand the different parts of the backup script.</p>
<ol>
<li><p>Configuration File: The script expects the path to the configuration file to be set in the <code>BACKUP_CONFIG_PATH</code> environment variable. It uses the Viper library to read the configuration values from the file.</p>
</li>
<li><p>Backup Settings: The script reads the necessary backup settings from the configuration file, such as the directory path to be backed up, the destination server, the backup type (<code>scp</code> or <code>rclone</code>), and the desired name for the backup archive file. Make sure to replace these values with your own in the script.</p>
</li>
<li><p>Creating the Archive: The script uses the <code>tar</code> command to create a compressed tar archive of the specified directory using the <code>tar -czf</code> command.</p>
</li>
<li><p>Copying the Archive: Depending on the backup type specified in the configuration file, the script either uses the <code>scp</code> command (for <code>scp</code> backup) or the <code>rclone</code> command (for <code>rclone</code> backup) to copy the archive to the destination server. If the copying process fails, it removes the local archive file.</p>
</li>
<li><p>Deleting the Local Archive: After successfully copying the archive to the server, the script deletes the local archive file to save disk space.</p>
</li>
</ol>
<h3 id="heading-running-the-backup-script"><strong>Running the Backup Script</strong></h3>
<p>To run the backup script, follow these steps:</p>
<ol>
<li><p>Save the script to a file named <code>backup.go</code> in a directory of your choice.</p>
</li>
<li><p>Set the <code>BACKUP_CONFIG_PATH</code> environment variable to the path of your configuration file. For example, you can run the following command in the terminal:</p>
<pre><code class="lang-go"> export BACKUP_CONFIG_PATH=/path/to/backup_config.yaml
</code></pre>
</li>
<li><p>Open a terminal and navigate to the directory containing the <code>backup.go</code> file.</p>
</li>
<li><p>Build and run the script using the <code>go run</code> command:</p>
<pre><code class="lang-go"> <span class="hljs-keyword">go</span> run backup.<span class="hljs-keyword">go</span>
</code></pre>
</li>
<li><p>The script will read the configuration file, create a backup archive of the specified directory, and copy it to the destination server using the specified backup type. The progress and any errors will be displayed in the terminal.</p>
</li>
<li><p>Once the backup process is complete, the local archive file will be deleted, and a success message will be displayed.</p>
</li>
</ol>
<p>Congratulations! You have successfully automated the backup process using Go.</p>
<h3 id="heading-conclusion"><strong>Conclusion</strong></h3>
<p>We wrote a backup script that reads the backup settings from a configuration file, creates a compressed tar archive of a specified directory, and copies it to a destination server using either <code>scp</code> or <code>rclone</code>. By following the steps outlined in this guide, you can ensure the safety and availability of your important files through automated backups.</p>
<p>Git repository link can be found <a target="_blank" href="https://github.com/manitaggarwal/backup-script-in-go">here.</a></p>
]]></content:encoded></item><item><title><![CDATA[Wallet Service in Spring Boot]]></title><description><![CDATA[Loyalty programs have become a popular way for businesses to retain customers and keep them engaged with their products or services. These programs are designed to reward loyal customers for their repeat business, which in turn, helps businesses incr...]]></description><link>https://manitaggarwal.com/wallet-service</link><guid isPermaLink="true">https://manitaggarwal.com/wallet-service</guid><category><![CDATA[Springboot]]></category><category><![CDATA[Kotlin]]></category><category><![CDATA[loyaltyprograms]]></category><category><![CDATA[wallet]]></category><dc:creator><![CDATA[Manit Aggarwal]]></dc:creator><pubDate>Wed, 05 Apr 2023 08:11:23 GMT</pubDate><content:encoded><![CDATA[<p>Loyalty programs have become a popular way for businesses to retain customers and keep them engaged with their products or services. These programs are designed to reward loyal customers for their repeat business, which in turn, helps businesses increase customer retention rates, sales, and overall revenue.</p>
<p>In a Loyalty Management System (LMS), a wallet is a digital account that holds loyalty points or rewards earned by a customer. Loyalty program wallets are used to track and manage a customer's loyalty rewards, which can be redeemed for discounts, special offers, or other incentives.</p>
<p>In this blog post, we'll discuss how to write a wallet for LMS, using a Spring Boot program written using Reactor Netty.</p>
<p>The program has the following features:</p>
<ol>
<li><p>The program allows businesses to create a loyalty program that rewards customers for their repeat business. Customers can earn these points by making purchases or performing certain actions.</p>
</li>
<li><p>Once customers have enrolled in the loyalty program, and request is received to award the points the program creates a wallet for them with a certain expiry date. The wallet holds the customer's earned points or rewards, which can be redeemed for discounts or other incentives.</p>
</li>
<li><p>The program automatically maintains a summary of the wallet for each loyalty program, which makes it easier for businesses to keep track of their customers' balances.</p>
</li>
</ol>
<h3 id="heading-creating-a-wallet">Creating a Wallet</h3>
<pre><code class="lang-kotlin"><span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">createWallet</span><span class="hljs-params">(
    createWalletRequest: <span class="hljs-type">CreateWalletRequest</span>,
    program: <span class="hljs-type">Program</span>
)</span></span>: Mono&lt;Wallet&gt; {
    validateCreateWalletRequest(createWalletRequest)
    <span class="hljs-keyword">return</span> walletRepository.save(
        Wallet(
            createWalletRequest.userId,
            createWalletRequest.earned,
            createWalletRequest.spent,
            <span class="hljs-number">0</span>,
            WalletStatus.ACTIVE,
            program.id,
            createWalletRequest.expiryDate,
            createWalletRequest.transactionId
        )
    ).doOnSuccess(<span class="hljs-keyword">this</span>::summarizeWallet)
        .onErrorResume(DuplicateKeyException::<span class="hljs-keyword">class</span>.java) {
            Mono.error(
                IllegalArgumentException(
                    <span class="hljs-string">"Wallet already exists for transaction <span class="hljs-subst">${createWalletRequest.transactionId}</span>"</span>
                )
            )
        }
}
</code></pre>
<h3 id="heading-creating-a-wallet-summary">Creating a Wallet Summary</h3>
<pre><code class="lang-kotlin"><span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">summarizeWallet</span><span class="hljs-params">(wallet: <span class="hljs-type">Wallet</span>)</span></span> {
    walletSummaryRepository
        .findWalletSummaryByUserIdAndProgram(wallet.userId, wallet.program)
        .flatMap { walletSummary -&gt;
            walletSummary.balance += (wallet.earned - wallet.spent)
            walletSummaryRepository.save(walletSummary)
        }
        .switchIfEmpty(
            walletSummaryRepository.save(
                WalletSummary(
                    wallet.userId,
                    wallet.earned - wallet.spent,
                    WalletStatus.ACTIVE,
                    wallet.program
                )
            )
        )
        .subscribe()
}
</code></pre>
<h3 id="heading-consuming-from-wallet-and-updating-wallet-summary">Consuming from Wallet and updating Wallet Summary</h3>
<pre><code class="lang-kotlin"><span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">consumeWallet</span><span class="hljs-params">(
    consumeWalletRequest: <span class="hljs-type">ConsumeWalletRequest</span>,
    program: <span class="hljs-type">Program</span>
)</span></span>: Flux&lt;Wallet&gt; {
    validateConsumeWalletRequest(consumeWalletRequest, program)
    <span class="hljs-keyword">return</span> walletRepository.findByUserIdAndProgramAndWalletStatusOrderById(
        consumeWalletRequest.userId,
        program.id,
        WalletStatus.ACTIVE
    ).collectList()
        .flatMapMany { wallets -&gt;
            <span class="hljs-keyword">val</span> consumedAmount = consumeWalletRequest.amount
            <span class="hljs-keyword">var</span> remainingAmount = consumedAmount
            <span class="hljs-keyword">val</span> consumedWallets = mutableListOf&lt;Wallet&gt;()
            wallets.forEach { wallet -&gt;
                <span class="hljs-keyword">if</span> (remainingAmount &gt; <span class="hljs-number">0</span>) {

                    <span class="hljs-keyword">val</span> amountToConsume =
                        remainingAmount.coerceAtMost((wallet.earned - wallet.spent))
                    wallet.spent += amountToConsume
                    <span class="hljs-keyword">if</span> (wallet.spent == wallet.earned) {
                        wallet.walletStatus = WalletStatus.INACTIVE
                    }
                    remainingAmount -= amountToConsume
                    consumedWallets.add(wallet)
                }
            }
            <span class="hljs-keyword">if</span> (remainingAmount &gt; <span class="hljs-number">0</span>) {
                <span class="hljs-keyword">throw</span> IllegalArgumentException(<span class="hljs-string">"Insufficient balance"</span>)
            }
            walletRepository.saveAll(consumedWallets)
        }.doOnComplete { updateSummaryWallet(consumeWalletRequest, program) }
}
</code></pre>
<p>So, why are loyalty programs important for businesses? Here are some of the key benefits:</p>
<ol>
<li><p>Increased Customer Retention: Loyalty programs encourage customers to come back to a business and continue purchasing their products or services. This results in increased customer retention rates, which can help businesses build a loyal customer base.</p>
</li>
<li><p>Increased Sales: When customers are rewarded for their repeat business, they are more likely to continue purchasing from a business. This can lead to increased sales and revenue for the business.</p>
</li>
<li><p>Better Customer Insights: Loyalty programs can provide businesses with valuable insights into their customers' behavior, preferences, and needs. This information can be used to improve products and services and to create more targeted marketing campaigns.</p>
</li>
<li><p>Competitive Advantage: Businesses that offer loyalty programs have a competitive advantage over those that do not. Customers are more likely to choose a business that offers rewards for their repeat business, which can help businesses stand out in a crowded marketplace.</p>
</li>
</ol>
<p>In conclusion, loyalty programs are a powerful tool for businesses to retain customers, increase sales, and gain valuable customer insights. By leveraging the benefits of loyalty programs, businesses can build strong, lasting relationships with their customers and grow their business over time.</p>
<p>As always working code can be found on <a target="_blank" href="https://github.com/manitaggarwal/wallet-service">github</a>.</p>
]]></content:encoded></item><item><title><![CDATA[gRPC Implementation in Spring Boot]]></title><description><![CDATA[gRPC is a high-performance open-source framework that provides an efficient and easy-to-use mechanism for building distributed systems. gRPC allows for seamless communication between services and is particularly well-suited for modern cloud-native ap...]]></description><link>https://manitaggarwal.com/grpc-implementation-in-spring-boot</link><guid isPermaLink="true">https://manitaggarwal.com/grpc-implementation-in-spring-boot</guid><category><![CDATA[gRPC]]></category><category><![CDATA[Springboot]]></category><category><![CDATA[Java]]></category><dc:creator><![CDATA[Manit Aggarwal]]></dc:creator><pubDate>Wed, 22 Mar 2023 07:29:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1679470086438/9299dba3-46a4-41eb-a63a-7c8e1f84c90d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>gRPC is a high-performance open-source framework that provides an efficient and easy-to-use mechanism for building distributed systems. gRPC allows for seamless communication between services and is particularly well-suited for modern cloud-native applications. In this post, we'll explore the benefits of gRPC and how to use it in Java.</p>
<h3 id="heading-benefits-of-grpc">Benefits of gRPC:</h3>
<ul>
<li><p><strong>Language Agnostic:</strong> One of the most significant benefits of gRPC is that it's language agnostic. gRPC provides libraries for multiple programming languages, including Java, Python, C++, and many others. This feature enables services written in different languages to communicate seamlessly.</p>
</li>
<li><p><strong>High Performance:</strong> gRPC is built on top of HTTP/2, which makes it incredibly fast and efficient. HTTP/2 provides multiple benefits like server push, request multiplexing, and header compression, which makes gRPC a high-performance framework.</p>
</li>
<li><p><strong>Easy to Use:</strong> gRPC has a straightforward API, which makes it easy to use. The framework provides features like request/response streaming, bidirectional streaming, and server-side streaming. These features make it simple to build complex distributed systems.</p>
</li>
<li><p><strong>Interoperability:</strong> gRPC allows for seamless communication between different systems, which enables services to work together even if they are written in different languages. gRPC uses a well-defined protocol buffer format to ensure interoperability.</p>
</li>
</ul>
<h3 id="heading-using-grpc-in-java">Using gRPC in Java:</h3>
<ul>
<li><p>Define the service using protocol buffers: Protocol buffers are used to define the service contract, data models, and messages. Protocol buffers are used to generate the server and client code.</p>
<p>  Example: proto file for a unary service.</p>
<pre><code class="lang-plaintext">  syntax = "proto3";

  package greeting;

  option java_package = "com.manitaggarwal.grpcclient";
  option java_multiple_files = true;

  message GreetingRequest {
    string firstName = 1;
  }

  message GreetingResponse {
    string result = 1;
  }

  service GreetingService {
    rpc greet(GreetingRequest) returns (GreetingResponse);
  }
</code></pre>
</li>
<li><p>Generate the server and client code: Once you have defined the service using protocol buffers, you need to generate the server and client code. The code generation is done using the protoc compiler, which generates the code in the language of your choice.</p>
<p>  Dependencies for gRPC in maven.</p>
<pre><code class="lang-xml">  <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>io.grpc<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>grpc-netty-shaded<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>1.53.0<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>runtime<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>io.grpc<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>grpc-protobuf<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>1.53.0<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>io.grpc<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>grpc-stub<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>1.53.0<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span> <span class="hljs-comment">&lt;!-- necessary for Java 9+ --&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.tomcat<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>annotations-api<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>6.0.53<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>provided<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
</code></pre>
<p>  Plugin requirements</p>
<pre><code class="lang-xml">  <span class="hljs-tag">&lt;<span class="hljs-name">build</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">extensions</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">extension</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>kr.motd.maven<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>os-maven-plugin<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>1.7.1<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">extension</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">extensions</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">plugins</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">plugin</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.xolstice.maven.plugins<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>protobuf-maven-plugin<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>0.6.1<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">configuration</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">protocArtifact</span>&gt;</span>com.google.protobuf:protoc:3.21.7:exe:${os.detected.classifier}<span class="hljs-tag">&lt;/<span class="hljs-name">protocArtifact</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">pluginId</span>&gt;</span>grpc-java<span class="hljs-tag">&lt;/<span class="hljs-name">pluginId</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">pluginArtifact</span>&gt;</span>io.grpc:protoc-gen-grpc-java:1.52.1:exe:${os.detected.classifier}<span class="hljs-tag">&lt;/<span class="hljs-name">pluginArtifact</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">configuration</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">executions</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">execution</span>&gt;</span>
                      <span class="hljs-tag">&lt;<span class="hljs-name">goals</span>&gt;</span>
                          <span class="hljs-tag">&lt;<span class="hljs-name">goal</span>&gt;</span>compile<span class="hljs-tag">&lt;/<span class="hljs-name">goal</span>&gt;</span>
                          <span class="hljs-tag">&lt;<span class="hljs-name">goal</span>&gt;</span>compile-custom<span class="hljs-tag">&lt;/<span class="hljs-name">goal</span>&gt;</span>
                      <span class="hljs-tag">&lt;/<span class="hljs-name">goals</span>&gt;</span>
                  <span class="hljs-tag">&lt;/<span class="hljs-name">execution</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">executions</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">plugin</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">plugins</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">build</span>&gt;</span>
</code></pre>
</li>
<li><p>Implement the server: Once you have the server code, you can implement the server logic. The server logic is where you implement the business logic of your service.</p>
<pre><code class="lang-java">  <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GreetingServiceImpl</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">GreetingServiceGrpc</span>.<span class="hljs-title">GreetingServiceImplBase</span> </span>{

      <span class="hljs-meta">@Override</span>
      <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">greet</span><span class="hljs-params">(GreetingRequest request,
                        StreamObserver&lt;GreetingResponse&gt; responseObserver)</span> </span>{
          System.out.println(<span class="hljs-string">"request.getFirstName() = "</span> + request.getFirstName());
          responseObserver.onNext(GreetingResponse.newBuilder()
                  .setResult(<span class="hljs-string">"Hello "</span> + request.getFirstName())
                  .build());
          responseObserver.onCompleted();
      }
  }
</code></pre>
</li>
<li><p>Implement the client: Once you have the client code, you can use it to make requests to the server. The client code provides a simple API that makes it easy to communicate with the server.</p>
<pre><code class="lang-java">  <span class="hljs-meta">@SpringBootApplication</span>
  <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GrpcClientApplication</span> </span>{

      <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">static</span> String CHANNEL_NAME = <span class="hljs-string">"localhost"</span>;
      <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> SERVER_PORT = <span class="hljs-number">50051</span>;

      <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">static</span> List&lt;String&gt; list = List.of(<span class="hljs-string">"Dan"</span>, <span class="hljs-string">"Pan"</span>);

      <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> InterruptedException </span>{
          SpringApplication.run(GrpcClientApplication.class, args);
          ManagedChannel channel = ManagedChannelBuilder
                  .forAddress(CHANNEL_NAME, SERVER_PORT)
                  .usePlaintext()
                  .build();

          GreetingServiceGrpc.GreetingServiceBlockingStub stub =
                  newBlockingStub(channel);

          <span class="hljs-keyword">for</span> (String name : list) {
              GreetingResponse response =
                      stub.greet(GreetingRequest.newBuilder()
                              .setFirstName(name)
                              .build());

              System.out.println(response.getResult());
          }

          channel.shutdown();
      }
</code></pre>
</li>
</ul>
<h3 id="heading-conclusion">Conclusion:</h3>
<p>gRPC is a powerful framework that provides a high-performance and easy-to-use mechanism for building distributed systems. Its language-agnostic nature, high performance, easy-to-use API, and interoperability make it an excellent choice for building modern cloud-native applications. Using gRPC in Java is simple, and it provides a lot of benefits that make it an excellent choice for building complex distributed systems.</p>
<p>Code for the above can be found <a target="_blank" href="https://github.com/manitaggarwal/grpc-unary-example">here on the GitHub.</a></p>
]]></content:encoded></item><item><title><![CDATA[Rest Assured - Testing RESTful APIs]]></title><description><![CDATA[In today's software development world, testing has become an integral part of the development process. Testing helps to ensure the quality and reliability of software. In this blog post, we will discuss how to use Rest Assured for testing RESTful API...]]></description><link>https://manitaggarwal.com/rest-assured</link><guid isPermaLink="true">https://manitaggarwal.com/rest-assured</guid><category><![CDATA[Java]]></category><category><![CDATA[REST API]]></category><category><![CDATA[Rest Assured]]></category><category><![CDATA[Testing]]></category><category><![CDATA[Springboot]]></category><dc:creator><![CDATA[Manit Aggarwal]]></dc:creator><pubDate>Thu, 09 Mar 2023 08:26:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1678350468966/f3376504-b208-4b17-9fd4-4f55d5423c0b.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In today's software development world, testing has become an integral part of the development process. Testing helps to ensure the quality and reliability of software. In this blog post, we will discuss how to use Rest Assured for testing RESTful APIs.</p>
<h3 id="heading-testing-restful-apis-with-rest-assured">Testing RESTful APIs with Rest Assured</h3>
<p>Rest Assured is a Java-based library that simplifies the testing of RESTful APIs. It provides a simple and intuitive DSL (Domain-Specific Language) for writing tests for RESTful APIs. It also supports various HTTP methods such as GET, POST, PUT, DELETE, etc.</p>
<p>Let's look at an example of how to use Rest Assured to test a RESTful API. Consider the following endpoint:</p>
<pre><code class="lang-java"><span class="hljs-meta">@GetMapping(value = "/student/{studentId}", produces = MediaType.APPLICATION_JSON_VALUE)</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> Student <span class="hljs-title">getStudentByStudentId</span><span class="hljs-params">(<span class="hljs-meta">@PathVariable</span> String studentId)</span> </span>{
    <span class="hljs-keyword">return</span> studentService.getStudent(studentId);
}
</code></pre>
<p>This endpoint retrieves a student with a given student ID. To test this endpoint, we can write a test case using Rest Assured as follows:</p>
<pre><code class="lang-java"><span class="hljs-comment">/*
 * Given: Where a student exists in the system
 * When: Call is placed to retrieve the student using student id
 * Then: Details of the student are fetched correctly
 * */</span>

<span class="hljs-meta">@Test</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">getStudentWhenExists_ByStudentId</span><span class="hljs-params">()</span> </span>{

    <span class="hljs-comment">// given - prerequisites</span>
    String msisdn = getRandomNumber();
    String email = <span class="hljs-string">"harry@admin.com"</span>;
    String name = <span class="hljs-string">"Harry"</span>;
    Student response = (Student) JsonUtils.getObjectFromJson(
            APICall.addStudent(DefaultData.getAdminRequest(msisdn, email, name)).asString(),
            Student.class);
    Assert.assertEquals(<span class="hljs-string">"add assert"</span>, msisdn, response.getMsisdn());

    String studentId = response.getStudentId();

    <span class="hljs-comment">// when</span>
    Student studentFromDatabase = (Student) JsonUtils.getObjectFromJson(APICall.getStudent(studentId).asString(),
            Student.class);

    <span class="hljs-comment">// then</span>
    Assert.assertEquals(<span class="hljs-string">"get assert"</span>, studentFromDatabase.getStudentId(), studentId);

}
</code></pre>
<p>In the above test case, we are using the given to specify the parameters for the request. In this case, we are ensuring that the student whom we want to retrieve exists in the system.</p>
<p>Then we are using the when to send the GET request to the specified endpoint.</p>
<p>Finally, we are using the then to verify the response. We are verifying that the student retrieved is having the same ID as specified by us in the request.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>In this blog post, we discussed how to use Rest Assured. Official documentation for the rest assured can be found at <a target="_blank" href="https://github.com/rest-assured/rest-assured/wiki/GettingStarted">GettingStarted · rest-assured/rest-assured Wiki (</a><a target="_blank" href="http://github.com">github.com</a><a target="_blank" href="https://github.com/rest-assured/rest-assured/wiki/GettingStarted">)</a>.</p>
<p>Also, the complete code for this project can be found at <a target="_blank" href="https://github.com/manitaggarwal/spring-boot-rest-assured">manitaggarwal/spring-boot-rest-assured: Implementing Rest Assured (</a><a target="_blank" href="http://github.com">github.com</a><a target="_blank" href="https://github.com/manitaggarwal/spring-boot-rest-assured">)</a></p>
]]></content:encoded></item><item><title><![CDATA[Bloom Filters using Guava]]></title><description><![CDATA[What are bloom filters?
Bloom filters are a data structure used in computer science for efficient probabilistic set membership testing. In other words, given a set of elements, a Bloom filter can answer whether an element is not in the set or maybe i...]]></description><link>https://manitaggarwal.com/bloom-filters-using-guava</link><guid isPermaLink="true">https://manitaggarwal.com/bloom-filters-using-guava</guid><category><![CDATA[bloom filter]]></category><category><![CDATA[Java]]></category><category><![CDATA[Springboot]]></category><category><![CDATA[guava]]></category><dc:creator><![CDATA[Manit Aggarwal]]></dc:creator><pubDate>Sun, 12 Feb 2023 13:53:54 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1676210004821/d8e86649-539d-4c1f-a16c-efca1aa4ec09.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-what-are-bloom-filters">What are bloom filters?</h2>
<p>Bloom filters are a data structure used in computer science for efficient probabilistic set membership testing. In other words, given a set of elements, a Bloom filter can answer whether an element is not in the set or maybe in the set, but cannot say for sure whether it is definitely in the set.</p>
<p>A Bloom filter is a compact data structure that uses multiple hash functions to map elements to positions in a bit array. When an element is added to the set, its hash values are used to set bits in the bit array to 1. When checking if an element is in the set, its hash values are used to look up bits in the bit array. If all of the bits are 1, it is likely that the element is in the set, but there is a chance of false positives (i.e. a bit of information that says an element is in the set, when it is actually not).</p>
<p>To create a Bloom filter, you need to determine the size of the bit array and the number of hash functions to use. The size of the bit array will depend on the expected number of elements in the set and the desired false positive rate. The number of hash functions will depend on the size of the bit array and the desired false positive rate.</p>
<h2 id="heading-java-implementation">Java Implementation</h2>
<p>In Java, you can implement a Bloom filter using a library by Google called Guava. Here is an example implementation:</p>
<h3 id="heading-creating-a-filter-using-guava">Creating a filter using Guava:</h3>
<p>Here we are creating a Bloom filter for up to 1000 <em>Integers</em> and we can tolerate a one-per cent (0.01) probability of false positives.</p>
<pre><code class="lang-java">BloomFilter&lt;Integer&gt; sha256filter = BloomFilter.create(
        Funnels.integerFunnel(),
        <span class="hljs-number">1000</span>,
        <span class="hljs-number">0.01</span>);
</code></pre>
<h3 id="heading-hashing-the-string">Hashing the string</h3>
<p>Here we hash the <em>String to sha256 and get the first 3 numbers</em></p>
<pre><code class="lang-java"><span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">sha256hex</span><span class="hljs-params">(String originalString)</span> </span>{
    <span class="hljs-keyword">return</span> Hashing.sha256()
            .hashString(originalString, StandardCharsets.UTF_8)
            .toString().replaceAll(<span class="hljs-string">"[^0-9]"</span>, <span class="hljs-string">""</span>).substring(<span class="hljs-number">0</span>, <span class="hljs-number">3</span>);
}
</code></pre>
<h3 id="heading-saving-the-hash-in-the-filter">Saving the hash in the filter</h3>
<p>After we successfully hash the string, we save the hashed number to filter</p>
<pre><code class="lang-java"><span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">saveToSHA256Filter</span><span class="hljs-params">(String originalString)</span> </span>{
    sha256filter.put(Integer.parseInt(
            hashUtils.sha256hex(originalString)));
}
</code></pre>
<h3 id="heading-checking-filter">Checking filter</h3>
<pre><code class="lang-java"><span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">checkInSHA256Filter</span><span class="hljs-params">(String originalString)</span> </span>{
    <span class="hljs-keyword">return</span> sha256filter.mightContain(Integer.parseInt(
            hashUtils.sha256hex(originalString)));
}
</code></pre>
<p>You can find the implementation <a target="_blank" href="https://github.com/manitaggarwal/bloom-filter-java">here</a> on github.</p>
<h2 id="heading-references">References</h2>
<p><a target="_blank" href="https://github.com/google/guava">Github for Guava</a></p>
]]></content:encoded></item><item><title><![CDATA[Use MongoDB to store Java Application Logs]]></title><description><![CDATA[Introduction
MongoDB is a NoSQL database program and uses JSON-like documents with optional schemas. Which makes it exceptionally great for storing logs.
I wrote a simple spring boot microservice to consume logs using a REST API and a Kafka Listener ...]]></description><link>https://manitaggarwal.com/use-mongodb-to-store-java-application-logs</link><guid isPermaLink="true">https://manitaggarwal.com/use-mongodb-to-store-java-application-logs</guid><category><![CDATA[Java]]></category><category><![CDATA[MongoDB]]></category><category><![CDATA[Springboot]]></category><category><![CDATA[Apache Kafka]]></category><dc:creator><![CDATA[Manit Aggarwal]]></dc:creator><pubDate>Fri, 10 Feb 2023 08:40:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1676019337928/63d4a7b0-95c7-40fc-9cff-de867dc64611.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>MongoDB is a NoSQL database program and uses JSON-like documents with optional schemas. Which makes it exceptionally great for storing logs.</p>
<p>I wrote a simple spring boot microservice to consume logs using a REST API and a Kafka Listener and store them in MongoDB.</p>
<p>Using Java Generics, we can create a document to store any JSON information on the database.</p>
<h2 id="heading-rest-controller">REST Controller</h2>
<pre><code class="lang-java"><span class="hljs-meta">@PostMapping(value = "/logs", consumes = "application/json", produces = "text/plain")</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">addLogs</span><span class="hljs-params">(<span class="hljs-meta">@RequestBody</span> LoggingRequest&lt;?&gt; request)</span> </span>{
    loggingService.saveLogs(request);
}
</code></pre>
<h2 id="heading-request">Request</h2>
<pre><code class="lang-java"><span class="hljs-meta">@Data</span> <span class="hljs-comment">// using lombok</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LoggingRequest</span>&lt;<span class="hljs-title">T</span>&gt; </span>{
    String applicationName;
    T logs;
}
</code></pre>
<h2 id="heading-document">Document</h2>
<pre><code class="lang-java"><span class="hljs-meta">@Document("logfile")</span> <span class="hljs-comment">// using springframework.data.mongodb</span>
<span class="hljs-meta">@Data</span> <span class="hljs-comment">//using lombok</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LogFile</span>&lt;<span class="hljs-title">T</span>&gt; </span>{

    <span class="hljs-meta">@Id</span>
    <span class="hljs-keyword">private</span> String id;

    <span class="hljs-keyword">private</span> Date time;

    <span class="hljs-keyword">private</span> T data;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">LogFile</span><span class="hljs-params">(Date time, T data)</span> </span>{
        <span class="hljs-keyword">this</span>.time = time;
        <span class="hljs-keyword">this</span>.data = data;
    }
}
</code></pre>
<h2 id="heading-repository">Repository</h2>
<pre><code class="lang-java"><span class="hljs-comment">// using springframework.data.mongodb.repository.MongoRepository</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">LoggingRepository</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">MongoRepository</span>&lt;<span class="hljs-title">LogFile</span>&lt;?&gt;, <span class="hljs-title">String</span>&gt; </span>{
}
</code></pre>
<h2 id="heading-data-in-mongodb">Data in MongoDB</h2>
<pre><code class="lang-json">{
  <span class="hljs-attr">"time"</span>: {
    <span class="hljs-attr">"$date"</span>: {
      <span class="hljs-attr">"$numberLong"</span>: <span class="hljs-string">"1675923022932"</span>
    }
  },
  <span class="hljs-attr">"data"</span>: {
    <span class="hljs-attr">"applicationName"</span>: <span class="hljs-string">"monitoring"</span>,
    <span class="hljs-attr">"logs"</span>: {
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"success"</span>,
      <span class="hljs-attr">"message"</span>: <span class="hljs-string">"add.success"</span>
    },
    <span class="hljs-attr">"_class"</span>: <span class="hljs-string">"com.manitaggarwal.store.log.controller.request.LoggingRequest"</span>
  },
  <span class="hljs-attr">"_class"</span>: <span class="hljs-string">"com.manitaggarwal.store.log.document.LogFile"</span>
}

{
  <span class="hljs-attr">"time"</span>: {
    <span class="hljs-attr">"$date"</span>: {
      <span class="hljs-attr">"$numberLong"</span>: <span class="hljs-string">"1675915874047"</span>
    }
  },
  <span class="hljs-attr">"data"</span>: {
    <span class="hljs-attr">"applicationName"</span>: <span class="hljs-string">"monitoring"</span>,
    <span class="hljs-attr">"logs"</span>: {
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"error"</span>,
      <span class="hljs-attr">"trace"</span>: <span class="hljs-string">"customer.does.not.exists"</span>
    },
    <span class="hljs-attr">"_class"</span>: <span class="hljs-string">"com.manitaggarwal.store.log.controller.request.LoggingRequest"</span>
  },
  <span class="hljs-attr">"_class"</span>: <span class="hljs-string">"com.manitaggarwal.store.log.document.LogFile"</span>
}
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Data is stored in MongoDB and can be queried using filters.</p>
<pre><code class="lang-json">{ <span class="hljs-attr">"data.applicationName"</span> : <span class="hljs-string">"monitoring"</span>, <span class="hljs-attr">"data.logs.type"</span> : <span class="hljs-string">"success"</span> }
</code></pre>
<h2 id="heading-reference">Reference</h2>
<p><a target="_blank" href="https://github.com/manitaggarwal/logging-service">Github Repo Link</a></p>
]]></content:encoded></item><item><title><![CDATA[Deploying Spring Boot Application in Production Kubernetes]]></title><description><![CDATA[Introduction
Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”. Recently the fantastic team at Spring have been adding useful features so that repetitive typical features that are reta...]]></description><link>https://manitaggarwal.com/deploying-spring-boot-application-in-production-kubernetes</link><guid isPermaLink="true">https://manitaggarwal.com/deploying-spring-boot-application-in-production-kubernetes</guid><category><![CDATA[Kubernetes]]></category><category><![CDATA[Java]]></category><category><![CDATA[Springboot]]></category><category><![CDATA[Spring]]></category><dc:creator><![CDATA[Manit Aggarwal]]></dc:creator><pubDate>Thu, 31 Mar 2022 08:10:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1648714188180/ACpRsqP8-.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”. Recently the fantastic team at Spring have been adding useful features so that repetitive typical features that are retaliated to packaging and running the application are streamlined.</p>
<p>Creating an image which is compatible with Open Container Initiative is as simple as running mvn spring-boot:build-image, and the image will be built using a cloud native buildpack.</p>
<p>As we want to run our images in a production kubernetes cluster, we need to a few more files to our code and do some preparations on the cluster so that we can run the images as deployments in the k8s cluster.</p>
<h1 id="heading-adding-manifest-files">Adding manifest files</h1>
<p>To run application in k8s we need a deployment, service and an ingress file.</p>
<p>We can create a new folder in the root of the project, at the level of src folder, and call it as manifests. Then inside this folder we can make all of our files.</p>
<p>In my case the application is called monitoring.</p>
<pre><code><span class="hljs-comment"># deployment.yaml</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">monitoring</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">replicas:</span> <span class="hljs-number">1</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">matchLabels:</span>
      <span class="hljs-attr">app:</span> <span class="hljs-string">monitoring</span>
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">metadata:</span>
      <span class="hljs-attr">labels:</span>
        <span class="hljs-attr">app:</span> <span class="hljs-string">monitoring</span>
    <span class="hljs-attr">spec:</span>
      <span class="hljs-attr">containers:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">image:</span> <span class="hljs-string">ghcr.io/manitaggarwal/monitoring:latest</span>
          <span class="hljs-attr">imagePullPolicy:</span> <span class="hljs-string">Always</span>
          <span class="hljs-attr">name:</span> <span class="hljs-string">monitoring</span>
          <span class="hljs-attr">ports:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">8080</span>
      <span class="hljs-attr">imagePullSecrets:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">regcred</span>
</code></pre><pre><code><span class="hljs-comment"># service.yaml</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Service</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">monitoring</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">type:</span> <span class="hljs-string">ClusterIP</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">monitoring</span>
  <span class="hljs-attr">ports:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">port:</span> <span class="hljs-number">8080</span>
      <span class="hljs-attr">targetPort:</span> <span class="hljs-number">8080</span>
</code></pre><pre><code><span class="hljs-comment"># ingress.yaml</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">networking.k8s.io/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Ingress</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">monitoring</span>
  <span class="hljs-attr">annotations:</span>
    <span class="hljs-attr">kubernetes.io/ingress.class:</span> <span class="hljs-string">nginx</span>
    <span class="hljs-attr">cert-manager.io/cluster-issuer:</span> <span class="hljs-string">"letsencrypt-prod"</span>
  <span class="hljs-attr">namespace:</span> <span class="hljs-string">default</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">tls:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">hosts:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">subdomain.example.com</span>
      <span class="hljs-attr">secretName:</span> <span class="hljs-string">monitoring-tls</span>
  <span class="hljs-attr">rules:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">host:</span> <span class="hljs-string">subdomain.example.com</span>
      <span class="hljs-attr">http:</span>
        <span class="hljs-attr">paths:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-attr">path:</span> <span class="hljs-string">/</span>
            <span class="hljs-attr">pathType:</span> <span class="hljs-string">Prefix</span>
            <span class="hljs-attr">backend:</span>
              <span class="hljs-attr">service:</span>
                <span class="hljs-attr">name:</span> <span class="hljs-string">monitoring</span>
                <span class="hljs-attr">port:</span>
                  <span class="hljs-attr">number:</span> <span class="hljs-number">8080</span>
</code></pre><h1 id="heading-preparing-k8s-cluster">Preparing k8s cluster</h1>
<p>As we want to reach our service and use a domain name to access it we will need an ingress controller. I have another post where I have detailed how to install ingress-nginx, in more detail.</p>
<p>To install ingress-nginx Ingress Nginx in itself will not give us the ability to generate SSL certificates, for that we need cert manager.</p>
<pre><code>helm repo add ingress<span class="hljs-operator">-</span>nginx https:<span class="hljs-comment">//kubernetes.github.io/ingress-nginx</span>
helm repo update

helm install ingress<span class="hljs-operator">-</span>nginx ingress<span class="hljs-operator">-</span>nginx<span class="hljs-operator">/</span>ingress<span class="hljs-operator">-</span>nginx
</code></pre><p>To install cert manager</p>
<pre><code>kubectl create namespace cert<span class="hljs-operator">-</span>manager
helm install cert<span class="hljs-operator">-</span>manager jetstack<span class="hljs-operator">/</span>cert<span class="hljs-operator">-</span>manager <span class="hljs-operator">-</span><span class="hljs-operator">-</span>namespace cert<span class="hljs-operator">-</span>manager <span class="hljs-operator">-</span><span class="hljs-operator">-</span>version v1<span class="hljs-number">.0</span><span class="hljs-number">.4</span> <span class="hljs-operator">-</span><span class="hljs-operator">-</span>set installCRDs<span class="hljs-operator">=</span><span class="hljs-literal">true</span>
</code></pre><p>To add Cert Issuer</p>
<pre><code><span class="hljs-attribute">apiVersion</span>: cert-manager.io/v1
<span class="hljs-attribute">kind</span>: ClusterIssuer
<span class="hljs-attribute">metadata</span>:
  <span class="hljs-attribute">name</span>: letsencrypt-prod
  <span class="hljs-attribute">namespace</span>: cert-manager
<span class="hljs-attribute">spec</span>:
  <span class="hljs-attribute">acme</span>:
    <span class="hljs-attribute">server</span>: <span class="hljs-attribute">https</span>:<span class="hljs-comment">//acme-v02.api.letsencrypt.org/directory</span>
    <span class="hljs-attribute">email</span>: manit<span class="hljs-variable">@example</span>.com
    <span class="hljs-attribute">privateKeySecretRef</span>:
      <span class="hljs-attribute">name</span>: letsencrypt-prod
    <span class="hljs-attribute">solvers</span>:
    - <span class="hljs-attribute">http01</span>:
        <span class="hljs-attribute">ingress</span>:
          <span class="hljs-attribute">class</span>: nginx
</code></pre><h1 id="heading-preparing-pipeline">Preparing pipeline</h1>
<p>We could manually apply the manifests to achieve our goal, but I am using Github Actions so that when I push to git repo the runner picks up the changes and builds and deploys the images to the desired k8s cluster.</p>
<pre><code><span class="hljs-attribute">name</span>: Java CI with Maven

<span class="solidity">on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

env:
  IMAGE_NAME: ghcr.io/manitaggarwal<span class="hljs-operator">/</span>monitoring:${{ github.sha }}

jobs:
  build:
    runs<span class="hljs-operator">-</span>on: ubuntu<span class="hljs-operator">-</span>latest
    steps:
    <span class="hljs-operator">-</span> uses: actions<span class="hljs-operator">/</span>checkout@v2
    <span class="hljs-operator">-</span> name: Set up JDK <span class="hljs-number">11</span>
      uses: actions<span class="hljs-operator">/</span>setup<span class="hljs-operator">-</span>java@v2
      with:
        java<span class="hljs-operator">-</span>version: <span class="hljs-string">'11'</span>
        distribution: <span class="hljs-string">'adopt'</span>
    <span class="hljs-operator">-</span> name: Login to GitHub Container Registry
      uses: docker<span class="hljs-operator">/</span>login<span class="hljs-operator">-</span>action@v1
      with:
        registry: ghcr.io
        username: ${{ github.actor }}
        password: ${{ secrets.GITHUB_TOKEN }}
    <span class="hljs-operator">-</span> name: Build with Maven
      run: <span class="hljs-operator">|</span> 
        mvn spring<span class="hljs-operator">-</span>boot:build<span class="hljs-operator">-</span>image
        docker tag monitoring:<span class="hljs-number">0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span><span class="hljs-operator">-</span>SNAPSHOT ${{ env.IMAGE_NAME }}
    <span class="hljs-operator">-</span> name: Push to Registry
      run: <span class="hljs-operator">|</span>
        docker push ${{ env.IMAGE_NAME }}
  deploy:
    needs: build
    runs<span class="hljs-operator">-</span>on: ubuntu<span class="hljs-operator">-</span>latest
    steps:
    <span class="hljs-operator">-</span> uses: actions<span class="hljs-operator">/</span>checkout@v2
    <span class="hljs-operator">-</span> uses: Azure<span class="hljs-operator">/</span>k8s<span class="hljs-operator">-</span>set<span class="hljs-operator">-</span>context@v1
      with:
        kubeconfig: ${{ secrets.KUBE_CONFIG }}
    <span class="hljs-operator">-</span> name: Run on Cluster
      run: <span class="hljs-operator">|</span>
        kubectl apply <span class="hljs-operator">-</span>f manifests<span class="hljs-operator">/</span>
        kubectl set image deployment<span class="hljs-operator">/</span>monitoring monitoring<span class="hljs-operator">=</span>${{ env.IMAGE_NAME }}</span>
</code></pre><h2 id="heading-sources">Sources</h2>
<p><a target="_blank" href="https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/">Pulling from private registry</a></p>
<p><a target="_blank" href="https://www.digitalocean.com/community/tutorials/how-to-set-up-an-nginx-ingress-with-cert-manager-on-digitalocean-kubernetes">Cert manager</a></p>
]]></content:encoded></item><item><title><![CDATA[Load balancer for bare metal k8s cluster]]></title><description><![CDATA[Summary
I have been using metal LB from quite some time now, even though the website still says the they are in beta, I have found it to be quite stable for my needs.
Why metal LB?
Kubernetes does not offer an implementation of network load-balancers...]]></description><link>https://manitaggarwal.com/load-balancer-for-bare-metal-k8s-cluster</link><guid isPermaLink="true">https://manitaggarwal.com/load-balancer-for-bare-metal-k8s-cluster</guid><category><![CDATA[Kubernetes]]></category><dc:creator><![CDATA[Manit Aggarwal]]></dc:creator><pubDate>Mon, 28 Mar 2022 06:51:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1648714332421/_y5QDC6Cl.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-summary">Summary</h1>
<p>I have been using metal LB from quite some time now, even though the website still says the they are in beta, I have found it to be quite stable for my needs.</p>
<h1 id="heading-why-metal-lb">Why metal LB?</h1>
<p>Kubernetes does not offer an implementation of network load-balancers (Services of type LoadBalancer) for bare metal clusters. The implementations of Network LB that Kubernetes does ship with are all glue code that calls out to various IaaS platforms (GCP, AWS, Azure…). If you’re not running on a supported IaaS platform (GCP, AWS, Azure…), LoadBalancers will remain in the “pending” state indefinitely when created.
Bare metal cluster operators are left with two lesser tools to bring user traffic into their clusters, “NodePort” and “externalIPs” services. Both of these options have significant downsides for production use, which makes bare metal clusters second class citizens in the Kubernetes ecosystem.
MetalLB aims to redress this imbalance by offering a Network LB implementation that integrates with standard network equipment, so that external services on bare metal clusters also “just work” as much as possible.</p>
<h1 id="heading-how-to-install-metal-lb-by-manifest">How to install metal LB by manifest?</h1>
<h2 id="heading-step-1-andgt-creating-a-namespace">Step 1 -&gt; Creating a namespace</h2>
<pre><code><span class="hljs-attribute">kubectl</span> apply -f https://raw.githubusercontent.com/metallb/metallb/v<span class="hljs-number">0</span>.<span class="hljs-number">10</span>.<span class="hljs-number">2</span>/manifests/namespace.yaml
</code></pre><h2 id="heading-step-2-andgt-installing-metal-lb">Step 2 -&gt; Installing metal lb</h2>
<pre><code><span class="hljs-attribute">kubectl</span> apply -f https://raw.githubusercontent.com/metallb/metallb/v<span class="hljs-number">0</span>.<span class="hljs-number">10</span>.<span class="hljs-number">2</span>/manifests/metallb.yaml
</code></pre><h2 id="heading-step-3-andgt-creating-a-config-file-and-setting-up-a-range-of-ip-addresses">Step 3 -&gt; Creating a config file and setting up a range of IP addresses</h2>
<pre><code><span class="hljs-attribute">apiVersion</span>: v1
<span class="hljs-attribute">kind</span>: ConfigMap
<span class="hljs-attribute">metadata</span>:
  <span class="hljs-attribute">namespace</span>: metallb-system
  <span class="hljs-attribute">name</span>: config
<span class="hljs-attribute">data</span>:
  <span class="hljs-attribute">config</span>: |
    <span class="hljs-attribute">address-pools</span>:
    - <span class="hljs-attribute">name</span>: default
      <span class="hljs-attribute">protocol</span>: layer2
      <span class="hljs-attribute">addresses</span>:
      - <span class="hljs-number">192.168</span>.<span class="hljs-number">1.240</span>-<span class="hljs-number">192.168</span>.<span class="hljs-number">1.250</span>
</code></pre><h2 id="heading-step-4-andgt-applying-the-configmap">Step 4 -&gt; Applying the ConfigMap</h2>
<pre><code><span class="hljs-selector-tag">kubectl</span> <span class="hljs-selector-tag">apply</span> <span class="hljs-selector-tag">-f</span> <span class="hljs-selector-tag">config</span><span class="hljs-selector-class">.yaml</span>
</code></pre><h2 id="heading-reference">Reference</h2>
<p><a target="_blank" href="https://metallb.universe.tf/">Official Documentation</a></p>
]]></content:encoded></item><item><title><![CDATA[Creating Single Node K8s Cluster]]></title><description><![CDATA[Cluster for development
Recently I wanted learn kubernetes and it was difficult to find a good development environment which I could use to play with and recreate within seconds when I break it.
I came across rancher which can be deployed on docker a...]]></description><link>https://manitaggarwal.com/creating-single-node-k8s-cluster</link><guid isPermaLink="true">https://manitaggarwal.com/creating-single-node-k8s-cluster</guid><category><![CDATA[Kubernetes]]></category><category><![CDATA[k8s]]></category><category><![CDATA[Docker]]></category><category><![CDATA[learning]]></category><dc:creator><![CDATA[Manit Aggarwal]]></dc:creator><pubDate>Fri, 25 Mar 2022 11:19:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1648207019706/nIfFO8Ec0.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-cluster-for-development">Cluster for development</h1>
<p>Recently I wanted learn kubernetes and it was difficult to find a good development environment which I could use to play with and recreate within seconds when I break it.</p>
<p>I came across rancher which can be deployed on docker as a single docker container and all persistent volume claims can be mapped to a docker volume so data can be preserved.</p>
<h1 id="heading-startup">Startup</h1>
<p>To start the docker container I created a run.sh file with following contents</p>
<pre><code><span class="hljs-meta">#!/bin/bash</span>
docker run -d --restart=unless-stopped --network=host \
-v /home/manit/rancher/config:/var/lib/rancher \
-v /home/manit/rancher/ssd:/ssd \
-v /mnt/disk/rancher/hdd:/hdd \
--name rancher \
--privileged rancher/rancher:latest
</code></pre><p>I am using host networking above so that when I deploy a kubernetes deployment with a server port it gets available on the localhost (or server public IP address).</p>
<p>I have 2 disks attached to the system, and main disk is an SSD, so I map out 2 directories from host to docker container so that I can store data from PVC where I want.</p>
<p>If you do not want to use host networking, you can use port 80 and 443 and publish them.</p>
<h1 id="heading-cleanup">Cleanup</h1>
<pre><code><span class="hljs-meta">#!/bin/bash</span>
docker container rm -f rancher
sudo rm -r ~/rancher/config ~/rancher/ssd /mnt/disk/rancher/hdd/
</code></pre><p>To save the data from kubernetes persistent volumes you may not remove ~/rancher/ssd and /mnt/disk/rancher/hdd.</p>
<p>This gave me an easy way to develop and clean out where something goes wrong.</p>
<p>My setup is running on old Lenovo Thinkpad which is currently connected to my home network.</p>
<h2 id="heading-references">References</h2>
<p><a target="_blank" href="https://rancher.com/docs/rancher/v2.5/en/installation/other-installation-methods/single-node-docker/">Rancher Docs</a></p>
]]></content:encoded></item><item><title><![CDATA[Using Spring Native]]></title><description><![CDATA[GraalVM is a Java VM and JDK based on HotSpot/OpenJDK, implemented in Java. It supports additional programming languages and execution modes, like ahead-of-time compilation of Java applications for fast startup and low memory footprint. The first pro...]]></description><link>https://manitaggarwal.com/using-spring-native</link><guid isPermaLink="true">https://manitaggarwal.com/using-spring-native</guid><category><![CDATA[Spring]]></category><category><![CDATA[Springboot]]></category><category><![CDATA[Java]]></category><category><![CDATA[native]]></category><dc:creator><![CDATA[Manit Aggarwal]]></dc:creator><pubDate>Fri, 25 Mar 2022 06:28:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1648188664893/iQxJkt31d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>GraalVM is a Java VM and JDK based on HotSpot/OpenJDK, implemented in Java. It supports additional programming languages and execution modes, like ahead-of-time compilation of Java applications for fast startup and low memory footprint. The first production-ready version, GraalVM 19.0, was released in May 2019. Problem with GraalVM is that it does not do well with Java Reflections, Proxies, Dependency Injections and a few other things. Where the Spring team with Spring Native has solved almost all the problems for a Restful Application.</p>
<p>A Typical Spring Boot project can be generated at <a target="_blank" href="https://start.spring.io/">Spring Initialiser</a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1648188915853/EadJBFMci.png" alt="Screenshot 2022-03-25 at 11.44.55 AM.png" /></p>
<p>Application JAR typically is compile using </p>
<blockquote>
<p>mvn clean package</p>
</blockquote>
<p>Which can now be swapped to</p>
<blockquote>
<p>mvn -Pnative clean package</p>
</blockquote>
<p>As the adding of spring native adds a special profile to the POM of the application.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1648189457422/eA2VZIrZr.png" alt="Screenshot 2022-03-25 at 11.53.52 AM.png" /></p>
<p>Which will create a platform specific binary, thus giving significant speed boost to the application at startup times and remove the requirement of JRE in the system running the binary.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1648189363877/wsq139RvR.png" alt="Screenshot 2022-03-25 at 11.49.42 AM.png" /></p>
<p>Project is hosted on my github, which can be found <a target="_blank" href="https://github.com/manitaggarwal/spring-native-testing">here</a>.</p>
]]></content:encoded></item></channel></rss>