<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Shaaf Syed</title>
    <description>The latest articles on Forem by Shaaf Syed (@sshaaf).</description>
    <link>https://forem.com/sshaaf</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F371656%2F62a7bfdc-3b24-4c74-8f1b-bbc5cd073836.png</url>
      <title>Forem: Shaaf Syed</title>
      <link>https://forem.com/sshaaf</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/sshaaf"/>
    <language>en</language>
    <item>
      <title>Modernizing Legacy Code with Konveyor AI: From EJB to Kubernetes</title>
      <dc:creator>Shaaf Syed</dc:creator>
      <pubDate>Wed, 15 Apr 2026 11:06:09 +0000</pubDate>
      <link>https://forem.com/sshaaf/modernizing-legacy-code-with-konveyor-ai-from-ejb-to-kubernetes-3d70</link>
      <guid>https://forem.com/sshaaf/modernizing-legacy-code-with-konveyor-ai-from-ejb-to-kubernetes-3d70</guid>
      <description>&lt;p&gt;I always enjoy participating in KubeCon. This time it was at the RAI center in Amsterdam. I have been to many conferences and the ones that are the best IMHO are the ones that are very community focused. For example DevNexus for Java, GeeCon for Geeks ;), and obviously KubeCon for everything Kubernetes. And obvsiouly making new friends and connections is a great way of learning from all the cool stuff thats going on. Thats probably enough name dropping for a wednesday ;) &lt;/p&gt;

&lt;p&gt;I had the opportunity to represent the &lt;a href="https://konveyor.io/" rel="noopener noreferrer"&gt;Konveyor Community&lt;/a&gt; project update. &lt;/p&gt;

&lt;h2&gt;
  
  
  Modernizing Legacy Code with Konveyor AI: From EJB to Kubernetes
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;"We want to have a meaningful result out of generative AI, not just chatting around... we can context engineer that code, send it into the LLM, and get results integrated back into the codebase." — &lt;strong&gt;Shaaf Syed&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Application modernization is a daunting task, especially when dealing with legacy codebases that are decades old. In my recent CNCF Project Lightning Talk, I introduced how the &lt;strong&gt;Konveyor&lt;/strong&gt; community is leveraging AI and static analysis to automate this process for almost any programming language. While there are multiple ways of solving the migration and modernization challenges, &lt;a href="https://konveyor.io/" rel="noopener noreferrer"&gt;Konveyor community&lt;/a&gt; takes a unique approach to it. &lt;/p&gt;

&lt;p&gt;For example many enterprises still rely on 30-year-old Enterprise Java Beans (EJB) or legacy protocols like RMI/IIOP. When leadership asks to move these applications to Kubernetes, developers face a nightmare of serialization issues, stubs, and complex clustering logic that simply doesn't fit a modern cloud-native environment. It like you just encountered the "Legacy Wall!". Nothing can get passed that point. True, but hardly anymore IMHO. Why not use the traditional static code anaylsis to enahnce the responses of an LLM. &lt;/p&gt;

&lt;p&gt;During the talk, I demonstrated a live example of an outdated EJB, i.e. being transformed into a modern REST service. What would normally take hours of manual refactoring was reduced to a generated &lt;strong&gt;Git patch&lt;/strong&gt; that the developer could simply review and apply.&lt;/p&gt;

&lt;p&gt;{{&amp;lt; youtube 6FNR4jGox9w &amp;gt;}}&lt;/p&gt;

&lt;p&gt;Konveyor solves this by combining deep static code analysis with the power of Large Language Models (LLMs). The core workflow involves:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Static Code Analysis:&lt;/strong&gt; The engine analyzes source code (Java, Go, Python, NodeJS, etc.) to identify "incidents"—specific lines of code that won't run on Kubernetes.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Context Engineering:&lt;/strong&gt; By understanding the code paths via the Language Server Protocol (LSP), Konveyor provides the necessary context to an LLM.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Automated Remediation:&lt;/strong&gt; Instead of just chatting, the AI generates meaningful code fixes, such as replacing legacy protocols with REST endpoints or suggesting the use of Kubernetes Secrets and ConfigMaps.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Beyond Chat: Agents and Memory
&lt;/h3&gt;

&lt;p&gt;What makes Konveyor AI particularly powerful is its move toward &lt;strong&gt;Agentic AI&lt;/strong&gt; and &lt;strong&gt;Distributed Memory&lt;/strong&gt;:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Agentic Validation
&lt;/h4&gt;

&lt;p&gt;When AI fixes one part of the code, it often breaks another. Konveyor’s agents handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Compilation:&lt;/strong&gt; Ensuring the new code actually builds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validation Testing:&lt;/strong&gt; Running tests to verify functionality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sanitization:&lt;/strong&gt; Cleaning up the output before it reaches the developer.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2. Organizational Memory
&lt;/h4&gt;

&lt;p&gt;If a developer modifies an AI-generated fix (e.g., changing how exceptions are handled), Konveyor can "remember" that preference. This memory is shared across the organization, so future migrations automatically follow the team's specific coding standards.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Involved
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://konveyor.io/" rel="noopener noreferrer"&gt;Konveyor&lt;/a&gt; is an open-source community project. You can find them at their CNCF kiosk or join the community to help build the future of automated application modernization.&lt;/p&gt;

</description>
      <category>java</category>
      <category>llm</category>
      <category>tools</category>
    </item>
    <item>
      <title>Nano Agent, Mega Senses: Adding LSP to the 260-Line Coding Agent</title>
      <dc:creator>Shaaf Syed</dc:creator>
      <pubDate>Fri, 10 Apr 2026 05:19:09 +0000</pubDate>
      <link>https://forem.com/sshaaf/nano-agent-mega-senses-adding-lsp-to-the-260-line-coding-agent-3343</link>
      <guid>https://forem.com/sshaaf/nano-agent-mega-senses-adding-lsp-to-the-260-line-coding-agent-3343</guid>
      <description>&lt;p&gt;Learn, learn, and learn more—that’s the name of the game. Coding agents are innovating fast; things are getting bigger and, quite often, bloated. To understand what an agent is actually doing, I’ve found it’s best to go back to the basics. It takes a bit more time, but the expertise you gain along the way sets you up for the long haul." So here I read &lt;a href="https://xam.dk/blog/nanocode-coding-agent-in-260-lines-of-java/" rel="noopener noreferrer"&gt;Max's&lt;/a&gt; post and thought, how about add some more things to this. Fetching ideas... done.. Lets add LSP support. &lt;/p&gt;

&lt;p&gt;The original &lt;a href="https://github.com/1rgs/nanocode" rel="noopener noreferrer"&gt;nanocode&lt;/a&gt; idea is easy to lose in the hype: &lt;strong&gt;a coding agent is mostly a loop&lt;/strong&gt;. You send prompt plus tool definitions to the model; the model answers with text or tool calls; you execute tools, feed results back, and repeat. The heavy lifting is the model; your code is &lt;strong&gt;hands&lt;/strong&gt; (write, edit, shell) and &lt;strong&gt;eyes&lt;/strong&gt; (read, glob, grep).&lt;/p&gt;

&lt;p&gt;That pattern is how “real” agents are structured too — usually with more polish, safety rails, and often &lt;strong&gt;richer senses&lt;/strong&gt; than plain text search.&lt;/p&gt;

&lt;p&gt;This &lt;a href="https://github.com/sshaaf/nanocode" rel="noopener noreferrer"&gt;fork&lt;/a&gt; keeps that loop and the same six core tools. It adds an optional extra sense for Java workspaces: a &lt;strong&gt;language server&lt;/strong&gt;, so the model can ask project-aware questions instead of inferring everything from raw files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvarlr5ftdm7gl2r91a9j.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvarlr5ftdm7gl2r91a9j.jpg" alt="nanocode Java with LSP"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Does LSP fits the same architecture?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;grep&lt;/code&gt; and &lt;code&gt;read&lt;/code&gt; are universal and powerful. They are also &lt;strong&gt;syntax-blind&lt;/strong&gt;. A symbol might be a field, a local, an import alias, or a string that happens to match. For Java — especially with Maven or Gradle classpaths — the &lt;strong&gt;compiler’s view&lt;/strong&gt; of the project is not the same as “all lines containing this word.”&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://microsoft.github.io/language-server-protocol/" rel="noopener noreferrer"&gt;Language Server Protocol&lt;/a&gt; is the boring industry answer: one process holds the classpath, incremental errors, types, and navigation graph; the client sends small questions (&lt;code&gt;go to definition&lt;/code&gt;, &lt;code&gt;hover&lt;/code&gt;, &lt;code&gt;publishDiagnostics&lt;/code&gt;) and gets structured answers.&lt;/p&gt;

&lt;p&gt;nanocode is a thin client in that sense: it forwards requests and prints results. Wiring &lt;strong&gt;Eclipse JDT Language Server&lt;/strong&gt; (the same engine behind most Java editor extensions) is not a different &lt;em&gt;kind&lt;/em&gt; of product — it is &lt;strong&gt;narrower, deeper vision&lt;/strong&gt; for one language, and stays optional so a “single-file story” remains when you do not need Java semantics.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this fork adds (in practice)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Detection&lt;/strong&gt; — If the workspace contains &lt;code&gt;.java&lt;/code&gt; files, nanocode can offer to turn Java support on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local install&lt;/strong&gt; — On consent (or via an environment flag), it downloads JDT LS once into your user cache (&lt;code&gt;~/.cache/nanocode&lt;/code&gt; or &lt;code&gt;$XDG_CACHE_HOME/nanocode&lt;/code&gt;), not into the repo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Process + protocol&lt;/strong&gt; — A small &lt;a href="https://github.com/eclipse-lsp4j/lsp4j" rel="noopener noreferrer"&gt;LSP4J&lt;/a&gt; client drives the server; documents are synced before semantic queries so answers match what is on disk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Three extra tools&lt;/strong&gt; the model sees only when LSP is enabled:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;java_definition&lt;/code&gt;&lt;/strong&gt; — Where is this name bound? (1-based line/column, like many editors.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;java_hover&lt;/code&gt;&lt;/strong&gt; — Tooltip-grade material: Javadoc, type hints, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;java_diagnostics&lt;/code&gt;&lt;/strong&gt; — Compiler and analysis diagnostics for one file, or a roll-up across files the server has published.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The main script grew; supporting code lives alongside it so newcomers can still read the core agent in one place.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;  &lt;span class="cm"&gt;/**
   * Dispatch tool calls to their respective implementations.
   *
   * Routes tool execution based on the tool name. Basic tools (read, write, edit, glob, grep, bash)
   * are always available. Java LSP tools (java_definition, java_hover, java_diagnostics) require
   * the javaLsp flag to be enabled.
   *
   * @param name The name of the tool to execute
   * @param args JSON arguments for the tool
   * @param javaLsp Whether Java LSP tools are enabled
   * @return Tool execution result, or error message prefixed with ERROR_PREFIX
   */&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;runTool&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;JsonNode&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;javaLsp&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;switch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
              &lt;span class="c1"&gt;// Basic file and search tools&lt;/span&gt;
              &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"read"&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;toolRead&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
              &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"write"&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;toolWrite&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
              &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"edit"&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;toolEdit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
              &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"glob"&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;toolGlob&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
              &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"grep"&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;toolGrep&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
              &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"bash"&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;toolBash&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

              &lt;span class="c1"&gt;// Java LSP tools - require javaLsp flag&lt;/span&gt;
              &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"java_definition"&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;javaLsp&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                      &lt;span class="n"&gt;yield&lt;/span&gt; &lt;span class="no"&gt;ERROR_PREFIX&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"Java LSP not enabled"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
                  &lt;span class="n"&gt;yield&lt;/span&gt; &lt;span class="nf"&gt;toolJavaDefinition&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
              &lt;span class="o"&gt;}&lt;/span&gt;
              &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"java_hover"&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;javaLsp&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                      &lt;span class="n"&gt;yield&lt;/span&gt; &lt;span class="no"&gt;ERROR_PREFIX&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"Java LSP not enabled"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
                  &lt;span class="n"&gt;yield&lt;/span&gt; &lt;span class="nf"&gt;toolJavaHover&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
              &lt;span class="o"&gt;}&lt;/span&gt;
              &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"java_diagnostics"&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;javaLsp&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                      &lt;span class="n"&gt;yield&lt;/span&gt; &lt;span class="no"&gt;ERROR_PREFIX&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"Java LSP not enabled"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
                  &lt;span class="n"&gt;yield&lt;/span&gt; &lt;span class="nf"&gt;toolJavaDiagnostics&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
              &lt;span class="o"&gt;}&lt;/span&gt;

              &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;ERROR_PREFIX&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"unknown tool "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
          &lt;span class="o"&gt;};&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// Catch all exceptions and return as error message&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;ERROR_PREFIX&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * Java LSP tool: Go to the definition of a symbol at the specified location.
   *
   * @param args JSON with path (required), line (default 1), column (default 1)
   * @return Location information for the symbol's definition
   */&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;toolJavaDefinition&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JsonNode&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;JavaLspSupport&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;definition&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"path"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;asText&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"line"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;asInt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
              &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"column"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;asInt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * Java LSP tool: Get type information, signature, and documentation for a symbol at cursor.
   *
   * @param args JSON with path (required), line (default 1), column (default 1)
   * @return Type info, Javadoc, and signature details for the symbol
   */&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;toolJavaHover&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JsonNode&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;JavaLspSupport&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hover&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"path"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;asText&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"line"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;asInt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
              &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"column"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;asInt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * Java LSP tool: Get compiler errors and warnings for a file or all cached files.
   *
   * @param args JSON with optional path (empty string = all cached diagnostics)
   * @return List of compiler/LSP diagnostics
   */&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;toolJavaDiagnostics&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JsonNode&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;JavaLspSupport&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;diagnostics&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"path"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;asText&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Examples (prompts that now “make sense”)
&lt;/h2&gt;

&lt;p&gt;These are the sorts of tasks where &lt;strong&gt;text tools alone&lt;/strong&gt; are brittle, but &lt;strong&gt;JDT + the loop&lt;/strong&gt; line up nicely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Navigate instead of guessing&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“In &lt;code&gt;src/main/java/.../OrderService.java&lt;/code&gt;, around line 48, what type is &lt;code&gt;discount&lt;/code&gt;? Use java_hover at that position, then java_definition if it is a field.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The model no longer has to re-derive the type from imports and local inference across half a dozen files unless it wants to double-check.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Errors with a real classpath&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Run java_diagnostics on &lt;code&gt;PaymentMapper.java&lt;/code&gt; and fix whatever JDT reports; then run &lt;code&gt;mvn -q test&lt;/code&gt; in bash.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Diagnostics are tied to how the project is built, not to “did my regex find the word &lt;code&gt;error&lt;/code&gt; in the log.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Onboarding a module&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I’m new to this package. Summarize the main entry points: use glob for &lt;code&gt;*Application.java&lt;/code&gt;, read those files, and for the class that implements &lt;code&gt;Runnable&lt;/code&gt; or hosts &lt;code&gt;main&lt;/code&gt;, show java_hover on the superclass or key API types.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Read + LSP combine the way humans use an IDE: skim files, drill into symbols.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Guardrails before a large edit&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Before we rename this method, list java_diagnostics for the whole workspace (no path) and tell me if anything is already red.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You still do not get a one-click &lt;strong&gt;rename refactor&lt;/strong&gt; in this fork — but you get &lt;strong&gt;honest compiler state&lt;/strong&gt;, which is often enough to avoid stepping on a broken build.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;────────────────────────────────────────────────────────────────────────────────
❯ What does `GET_USERS` mean in `KeycloakOperation`?
────────────────────────────────────────────────────────────────────────────────

⏺ Java_hover(src/main/java/dev/shaaf/keycloak/mcp/server/KeycloakOperation.java)
  ⎿  dev ... +1 lines

⏺ Glob(**/KeycloakOperation.java)
  ⎿  ./src/main/java/dev/shaaf/keycloak/mcp/server/KeycloakOperat...

⏺ Read(src/main/java/dev/shaaf/keycloak/mcp/server/KeycloakOperation.java)
  ⎿     1| package dev.shaaf.keycloak.mcp.server; ... +67 lines

⏺ `GET_USERS` in the `KeycloakOperation` enum is a constant that represents an operation related to retrieving a list of users from the Keycloak server. This operation is part of a group of user operations defined in the enum, which allows users of the `KeycloakTool` to easily reference and execute actions related to user management, such as creating, updating, deleting users, and retrieving user details.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To ground this in something non-trivial, the same &lt;strong&gt;Java LSP stack&lt;/strong&gt; was exercised against &lt;strong&gt;&lt;code&gt;keycloak-mcp-server&lt;/code&gt;&lt;/strong&gt;: a Quarkus-based MCP server on Java 21, Maven BOM, and many command classes under &lt;code&gt;dev.shaaf.keycloak.mcp.server&lt;/code&gt;. Typical results: clean diagnostics on command base classes, classpath-backed &lt;strong&gt;hover&lt;/strong&gt; on &lt;code&gt;ObjectMapper&lt;/code&gt; (Jackson JAR version matching the build), and &lt;strong&gt;definition&lt;/strong&gt; jumping to the real &lt;code&gt;KeycloakCommand&lt;/code&gt; source — the sort of answers &lt;code&gt;grep&lt;/code&gt; alone cannot guarantee.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trying JDT without an LLM API key:&lt;/strong&gt; the fork ships &lt;code&gt;LspProbe.java&lt;/code&gt; (JBang), a tiny harness that calls the same &lt;code&gt;JavaLspSupport&lt;/code&gt; as nanocode’s &lt;code&gt;java_*&lt;/code&gt; tools. From the nanocode repo:&lt;br&gt;&lt;br&gt;
&lt;code&gt;jbang run LspProbe.java /path/to/your-maven-project&lt;/code&gt;&lt;br&gt;&lt;br&gt;
(optional: &lt;code&gt;LSP_PROBE_SYNC_SEC&lt;/code&gt; to tune wait for Maven sync.) First run may download JDT (~50 MB) into &lt;code&gt;~/.cache/nanocode/&lt;/code&gt;; after that you can confirm hover, definition, and diagnostics on disk before wiring up &lt;code&gt;ANTHROPIC_API_KEY&lt;/code&gt;, &lt;code&gt;OPENROUTER_API_KEY&lt;/code&gt;, or &lt;code&gt;OPENAI_API_KEY&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trade-offs worth saying out loud
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Weight&lt;/strong&gt; — JDT LS is not a toy; it is a real JVM product. The agent stays teachable; the server is the same class of dependency serious Java tooling already uses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tokens&lt;/strong&gt; — A richer tool list and longer system text cost a little each request; &lt;strong&gt;targeted&lt;/strong&gt; LSP results can replace huge file reads. Net savings are not guaranteed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trust&lt;/strong&gt; — &lt;code&gt;bash&lt;/code&gt; remains full power. LSP does not sandbox anything; it mainly reduces wrong guesses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nano&lt;/strong&gt; -- Nano but at about 4x more code from the original port.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Read it, run it, break it, extend it. It’s a great way to understand how tools like Claude Code, Cursor, and Co Pilot work under the hood.  -- &lt;a href="https://xam.dk/blog/nanocode-coding-agent-in-260-lines-of-java/" rel="noopener noreferrer"&gt;Max&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Nanocode with LSP - &lt;a href="https://github.com/sshaaf/nanocode" rel="noopener noreferrer"&gt;https://github.com/sshaaf/nanocode&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A Coding Agent in 260 Lines of Java - &lt;a href="https://xam.dk/blog/nanocode-coding-agent-in-260-lines-of-java/" rel="noopener noreferrer"&gt;https://xam.dk/blog/nanocode-coding-agent-in-260-lines-of-java/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Original demo agent: github.com/1rgs/nanocode - &lt;a href="https://github.com/1rgs/nanocode" rel="noopener noreferrer"&gt;https://github.com/1rgs/nanocode&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Max’s JBang port: github.com/maxandersen/nanocode - &lt;a href="https://github.com/maxandersen/nanocode" rel="noopener noreferrer"&gt;https://github.com/maxandersen/nanocode&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Eclipse JDT Language Server: eclipse-jdtls/eclipse.jdt.ls - &lt;a href="https://github.com/eclipse-jdtls/eclipse.jdt.ls" rel="noopener noreferrer"&gt;https://github.com/eclipse-jdtls/eclipse.jdt.ls&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>java</category>
      <category>llm</category>
      <category>tools</category>
    </item>
    <item>
      <title>Java + LLMs: A hands-on guide to building LLM Apps in Java with Jakarta</title>
      <dc:creator>Shaaf Syed</dc:creator>
      <pubDate>Sat, 08 Feb 2025 08:12:51 +0000</pubDate>
      <link>https://forem.com/sshaaf/java-llms-a-hands-on-guide-to-building-llm-apps-in-java-with-jakarta-4adf</link>
      <guid>https://forem.com/sshaaf/java-llms-a-hands-on-guide-to-building-llm-apps-in-java-with-jakarta-4adf</guid>
      <description>&lt;p&gt;&lt;a href="https://foojay.io/" rel="noopener noreferrer"&gt;Java&lt;/a&gt; is an amazing language to work with. Millions of developers use it for daily work routines, and many mission-critical applications run on Java today. Whether we talk about banks, stock exchanges, or space, Java is prevalent and a language of choice. &lt;/p&gt;

&lt;p&gt;With the advent of Large Language Models(LLM), new opportunities are at play. While Python has been the dominating language runtime for apparent reasons, there is a misconception that creating applications, agents, or other components for LLMs should also be done in Python. Most of the integration in LLMs is achieved by using REST API. Java is not short on anything in that space. System integration has been pretty awesome in Java, with many tools in that space. So why not Java? &lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.langchain4j.dev/" rel="noopener noreferrer"&gt;LangChain4J&lt;/a&gt;! The amazing developers and &lt;a href="https://github.com/langchain4j/langchain4j/graphs/contributors" rel="noopener noreferrer"&gt;contributors&lt;/a&gt; have provided many goodies for us to infuse LLMs into our apps. Use LangChain4J if you haven't done so yet. &lt;/p&gt;

&lt;p&gt;I had the opportunity to speak with &lt;a href="https://x.com/bazlur_rahman" rel="noopener noreferrer"&gt;A N M Bazlur&lt;/a&gt; about this topic at the JChampionsConf last week. &lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/4fFZjtDAXW0"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The source code and step-by-step guide is available here on &lt;a href="https://github.com/rokon12/llm-jakarta" rel="noopener noreferrer"&gt;github&lt;/a&gt;. &lt;br&gt;
And the &lt;a href="https://speakerdeck.com/sshaaf/java-plus-llms-a-hands-on-guide-to-building-llm-apps-in-java-with-jakarta" rel="noopener noreferrer"&gt;speakerdeck&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>java</category>
      <category>llm</category>
      <category>langchain</category>
      <category>ai</category>
    </item>
    <item>
      <title>Embracing the Future of Application Modernization with KAI</title>
      <dc:creator>Shaaf Syed</dc:creator>
      <pubDate>Mon, 29 Jul 2024 16:16:20 +0000</pubDate>
      <link>https://forem.com/sshaaf/embracing-the-future-of-application-modernization-with-kai-1hp</link>
      <guid>https://forem.com/sshaaf/embracing-the-future-of-application-modernization-with-kai-1hp</guid>
      <description>&lt;p&gt;&lt;a href="https://www.konveyor.io" rel="noopener noreferrer"&gt;Konveyor’s&lt;/a&gt; main strength lies in its comprehensive approach to migration and modernization. At the core of &lt;a href="https://www.konveyor.io" rel="noopener noreferrer"&gt;Konveyor’s&lt;/a&gt; functionality is its powerful analysis engine. This engine performs static source code analysis, identifying anti-patterns and issues that might hinder the application’s operation on a target platform. Utilizing community standards like the Language Server Protocol, Konveyor's analysis engine uses rules designed to aid in various migration scenarios. Users can also create custom rules to address specific migration needs, enhancing Konveyor's flexibility and adaptability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter Konveyor AI - "KAI"
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/konveyor/kai" rel="noopener noreferrer"&gt;Konveyor AI&lt;/a&gt;, the latest advancement under the Konveyor umbrella, leverages generative AI to further streamline application modernization. The primary goal of Konveyor AI (or Kai) is to automate source code changes, thereby improving the economics of migration and modernization efforts.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Our approach is to use static code analysis to find the areas in source code that need to be transformed. 'kai' will iterate through analysis information and work with LLMs to generate code changes to resolve incidents identified from analysis. This approach does not require fine-tuning of LLMs, we augment a LLMs knowledge via the prompt, similar to approaches with &lt;a href="https://arxiv.org/abs/2005.11401" rel="noopener noreferrer"&gt;RAG&lt;/a&gt; by leveraging external data from inside of Konveyor and from Analysis Rules to aid the LLM in constructing better results. For example, &lt;a href="https://github.com/konveyor/analyzer-lsp/blob/main/docs/rules.md" rel="noopener noreferrer"&gt;analyzer-lsp Rules&lt;/a&gt; such as these (&lt;a href="https://github.com/konveyor/rulesets/tree/main/default/generated/quarkus" rel="noopener noreferrer"&gt;Java EE to Quarkus rulesets&lt;/a&gt;) are leveraged to aid guiding a LLM to update a legacy Java EE application to Quarkus&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There is an &lt;a href="https://www.konveyor.io/blog/kai-deep-dive-2024/" rel="noopener noreferrer"&gt;awesome blog post&lt;/a&gt; by &lt;a href="https://github.com/jwmatthews" rel="noopener noreferrer"&gt;John Matthews&lt;/a&gt; one of the Lead engineers in the project explaining "Kai" in depth.&lt;/p&gt;

&lt;h3&gt;
  
  
  BYO Large Language Model
&lt;/h3&gt;

&lt;p&gt;One of the standout features of Kai is its model-agnostic approach. Unlike other solutions, Kai doesn’t bundle any specific large language model (LLM). Instead, it extends Konveyor to interact with various LLMs, providing the flexibility to use the best-suited model for each specific migration context. This approach ensures that organizations can optimize their migration strategies without being locked into a single technology.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical usecase: From Java EE to Quarkus
&lt;/h2&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/cXJPHdETwcY"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The short demonstration shows how Konveyor AI facilitates migrations from legacy frameworks like Java EE to modern solutions like Quarkus. The process begins with a static code analysis using the Konveyor CLI, which identifies migration issues within the codebase. Once the analysis is complete, Konveyor AI steps in to generate patches for the identified issues, leveraging LLMs to suggest precise code changes.&lt;/p&gt;

&lt;p&gt;For instance, outdated Java EE annotations are seamlessly replaced with their modern Jakarta EE counterparts, and JMS-based message-driven beans were converted to JakartaEE annotations, and legacy technology like EJBs turned into REST end points. These changes can be validated by a developer within an integrated development environment (IDE), showcasing how Konveyor AI integrates into existing developer workflows. Konveyor AI does this by using KAI extension.&lt;/p&gt;

&lt;p&gt;For a &lt;a href="https://www.youtube.com/watch?v=0eh-B55zMPI&amp;amp;t=1s" rel="noopener noreferrer"&gt;detailed demo and presentation&lt;/a&gt; watch this episode of &lt;a href="https://commons.openshift.org/" rel="noopener noreferrer"&gt;OpenShift commons&lt;/a&gt; where &lt;a href="https://www.linkedin.com/in/rromannissen/" rel="noopener noreferrer"&gt;Ramón&lt;/a&gt; and I go through the details.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Why should I use Konveyor AI's or how?&lt;/code&gt; the power of "Kai" lies in its ability to automate repetitive migration tasks, leveraging accumulated knowledge from previous migrations. By focusing on code transformation rather than architectural changes, Kai provides a robust tool for modernizing applications efficiently. It empowers developers to make informed decisions, enabling smoother transitions to modern frameworks and cloud-native environments.&lt;/p&gt;

&lt;p&gt;To give the latest build a try head out the to instructions &lt;a href="https://github.com/konveyor/kai" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Get involved in the &lt;a href="https://www.konveyor.io" rel="noopener noreferrer"&gt;Konveyor community&lt;/a&gt; through their mailing lists, &lt;a href="https://www.konveyor.io/slack/" rel="noopener noreferrer"&gt;Slack channels&lt;/a&gt;, and &lt;a href="https://www.youtube.com/@konveyor361" rel="noopener noreferrer"&gt;regular meetings&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>java</category>
      <category>migration</category>
      <category>ai</category>
      <category>llm</category>
    </item>
    <item>
      <title>Java monitoring: Exploring Cryostat 2.4 features on OpenShift</title>
      <dc:creator>Shaaf Syed</dc:creator>
      <pubDate>Mon, 18 Dec 2023 21:38:37 +0000</pubDate>
      <link>https://forem.com/sshaaf/java-monitoring-exploring-cryostat-24-features-on-openshift-5527</link>
      <guid>https://forem.com/sshaaf/java-monitoring-exploring-cryostat-24-features-on-openshift-5527</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Orignally posted at&lt;/em&gt; &lt;a href="https://developers.redhat.com/articles/2023/12/18/java-monitoring-exploring-cryostat-24-features-openshift" rel="noopener noreferrer"&gt;Red Hat Developers&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://developers.redhat.com/products/cryostat/overview" rel="noopener noreferrer"&gt;Red Hat's latest build of Cryostat 2.4&lt;/a&gt;, designed specifically for the &lt;a href="https://developers.redhat.com/products/openshift/overview" rel="noopener noreferrer"&gt;Red Hat OpenShift Container Platform&lt;/a&gt;, brings a wealth of features and enhancements that cater to various monitoring needs for &lt;a href="https://developers.redhat.com/java" rel="noopener noreferrer"&gt;Java&lt;/a&gt; applications.&lt;/p&gt;

&lt;p&gt;At its core, Cryostat 2.4 excels in comprehensive Java Flight Recorder (JFR) data management. Users can effortlessly start, stop, retrieve, archive, import, and export JFR data, all through an intuitive web console or an accessible HTTP API. This enhances the ease with which developers can handle JVM performance data. Moreover, Cryostat 2.4 provides flexibility in terms of data storage and analysis. Users can store and analyze JFR data directly on their Red Hat OpenShift or export it to external monitoring applications for a deeper dive into the data.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;ARM (aarch64)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A significant enhancement in Cryostat 2.4 is its support for Red Hat OpenShift Container Platform 4.11 and later versions, particularly for the ARM64 (aarch64) architecture. This broadens its applicability across various platforms, making it a versatile tool in diverse environments, e.g., edge deployments.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Smart triggering and HTTP API&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Another notable feature is the introduction of dynamic JFR recording with MBean custom triggers. The Cryostat agent is equipped with smart triggers that continuously monitor MBean counter values such as runtime, memory, thread, and operating system metrics. Users are able to set custom trigger conditions, thereby adding a layer of precision to JVM monitoring.&lt;/p&gt;

&lt;p&gt;Further enhancing its functionality, Cryostat 2.4 introduces an improved HTTP API provided by the Cryostat agent. This serves as an alternative to an application’s JMX port, allowing users to fully utilize Cryostat's features without the need for JMX port exposure in target applications. This is particularly beneficial for enhancing security and simplifying configurations.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;JAR distribution&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Cryostat 2.4 also offers flexibility in agent deployment. It provides two types of agent JAR file distributions—an all-in-one "shaded" JAR file that includes all dependencies, and a standard JAR file containing only the agent code. This choice caters to different user requirements and helps manage potential dependency conflicts more effectively.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Topology dashboard view&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Additionally, Cryostat 2.4 brings new features and fixes to enhance user experience. The Topology and Dashboard views in the Cryostat web console now display additional information about target JVMs, such as operating system name, memory statistics, class path, library paths, input arguments, and system properties. The introduction of a parameter to restart flight recordings offers more control over recording management, addressing a common user pain point.&lt;/p&gt;

&lt;p&gt;Significant issues from previous versions have also been addressed. Automated rule-triggering issues in discovered JVM targets have been resolved, with Cryostat performing regular rechecks and reattempts at rule triggering. This ensures a more reliable connection with JVMs, improving consistency and predictability. The agent registration protocol has also seen improvements, resolving issues related to agent registration with the Cryostat server and ensuring a smoother, more reliable process.&lt;/p&gt;

&lt;p&gt;Cryostat 2.4 is a testament to Red Hat's commitment to providing robust and innovative solutions for modern containerized applications. With its enhanced features, improved user experience, and greater flexibility, Cryostat 2.4 is poised to be a pivotal tool in JVM monitoring, offering a sophisticated and user-friendly approach to performance monitoring in containerized environments. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;How to use Cryostat for your Java workloads&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;You can &lt;a href="https://access.redhat.com/documentation/en-us/red_hat_build_of_cryostat/2/html/getting_started_with_cryostat/installing-cryostat-on-openshift-using-an-operator_cryostat" rel="noopener noreferrer"&gt;install the Red Hat build of Cryostat&lt;/a&gt; using our OpenShift operator, available in &lt;a href="https://developers.redhat.com/products/openshift/overview" rel="noopener noreferrer"&gt;Red Hat OpenShift&lt;/a&gt;'s Operator Hub.&lt;br&gt;
For non-production usage, you can also try our &lt;a href="https://developers.redhat.com/articles/2022/06/20/install-cryostat-new-helm-chart" rel="noopener noreferrer"&gt;Helm chart&lt;/a&gt;, included as part of OpenShift’s Helm chart repository.&lt;br&gt;
You can also try Red Hat build of Cryostat &lt;a href="https://developers.redhat.com/products/cryostat/getting-started" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Get support for Java&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Support for Cryostat, OpenJDK, and Eclipse Temurin is available to Red Hat customers through a subscription to&lt;a href="https://www.redhat.com/en/products/runtimes" rel="noopener noreferrer"&gt; Red Hat Runtimes&lt;/a&gt;, &lt;a href="https://developers.redhat.com/products/rhel/overview" rel="noopener noreferrer"&gt;Red Hat Enterprise Linux&lt;/a&gt;, and Red Hat OpenShift. Contact your local Red Hat representative or&lt;a href="https://www.redhat.com/en/about/contact/sales" rel="noopener noreferrer"&gt; Red Hat sales&lt;/a&gt; for more details. You can expect support for Java and other runtimes as described under the&lt;a href="https://access.redhat.com/support/policy/updates/jboss_notes/" rel="noopener noreferrer"&gt; Red Hat Product Update and Support Lifecycle&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Resources&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developers.redhat.com/java" rel="noopener noreferrer"&gt;Catch up with the latest on Java&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=rKG6nvk9xlE" rel="noopener noreferrer"&gt;Video: What is Eclipse Temurin?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://access.redhat.com/documentation/en-us/openjdk/17/html-single/getting_started_with_eclipse_temurin/index" rel="noopener noreferrer"&gt;Getting started with Eclipse Temurin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.redhat.com/en/blog/red-hat-joins-eclipse-adoptium-working-group" rel="noopener noreferrer"&gt;Red Hat joins the Eclipse Adoptium Working Group&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.redhat.com/en/blog/eclipse-adoptium-achieves-its-first-java-se-release" rel="noopener noreferrer"&gt;Eclipse Adoptium achieves its first Java SE release&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.redhat.com/en/about/press-releases/red-hat-introduces-commercial-support-openjdk-microsoft-windows" rel="noopener noreferrer"&gt;Red Hat Introduces Commercial Support for OpenJDK on Microsoft Windows&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.redhat.com/en/blog/history-and-future-openjdk" rel="noopener noreferrer"&gt;The history and future of OpenJDK&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>java</category>
      <category>monitoring</category>
      <category>container</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Whats new for developers in JDK 21</title>
      <dc:creator>Shaaf Syed</dc:creator>
      <pubDate>Thu, 21 Sep 2023 08:04:53 +0000</pubDate>
      <link>https://forem.com/sshaaf/whats-new-for-developers-in-jdk-21-hd8</link>
      <guid>https://forem.com/sshaaf/whats-new-for-developers-in-jdk-21-hd8</guid>
      <description>&lt;p&gt;In an exciting development for&lt;a href="https://developers.redhat.com/java" rel="noopener noreferrer"&gt; Java&lt;/a&gt; developers, this September 19th marked the release of JDK 21. This release contains many new capabilities that benefit the Java ecosystem, including virtual threads, record patterns, and sequenced collections. There are also some interesting features in the preview for JDK 21, such as string templates, scoped values, and structured concurrency. This article highlights six new features in this release.&lt;/p&gt;

&lt;h2&gt;
  
  
  Virtual threads
&lt;/h2&gt;

&lt;p&gt;Java's traditional threading model can quickly become an expensive operation if the application creates more threads than the operating system (OS) can handle. Also, in cases where the thread lifecycle is not long, the cost of creating a thread is high.&lt;/p&gt;

&lt;p&gt;Enter&lt;a href="https://openjdk.org/jeps/444" rel="noopener noreferrer"&gt; virtual threads&lt;/a&gt;, which solve this problem by mapping Java threads to carrier threads that manage (i.e., mount/unmount) thread operations to a carrier thread. In contrast, the carrier thread works with the OS thread. It is an abstraction that gives more flexibility and control for developers. See Figure 1.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq1szclhmk5kfw882y0fj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq1szclhmk5kfw882y0fj.png" alt="alt_text" width="800" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 1: The virtual threading model in JDK 21.&lt;/p&gt;

&lt;p&gt;The following is an example of virtual threads and a good contrast to OS/platform threads. The program uses the &lt;code&gt;ExecutorService&lt;/code&gt; to create 10,000 tasks and waits for all of them to be completed. Behind the scenes, the JDK will run this on a limited number of carrier and OS threads, providing you with the durability to write concurrent code with ease.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newVirtualThreadPerTaskExecutor&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;IntStream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;range&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10_000&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sleep&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;});&lt;/span&gt;
    &lt;span class="o"&gt;});&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;// executor.close() is called implicitly, and waits&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Structured concurrency (Preview)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://openjdk.org/jeps/453" rel="noopener noreferrer"&gt;Structured concurrency&lt;/a&gt; is closely tied to virtual threads, and it aims to eliminate common risks such as cancellation, shutdown, thread leaks, etc., by providing an API that enhances the developer experience. If a task splits into concurrent subtasks, then they should all return to the same place, i.e., the task's code block.&lt;/p&gt;

&lt;p&gt;In Figure 2, &lt;code&gt;findUser&lt;/code&gt; and &lt;code&gt;fetchOrder&lt;/code&gt; both need to execute to get the data from different services and then use that data to compose results and send it back in a response to the consumer. Normally, these tasks could be done concurrently and could be error prone if &lt;code&gt;findUser&lt;/code&gt; didn't return; &lt;code&gt;fetchOrder&lt;/code&gt; would need to wait for it to complete, and then finally execute the Join operations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9cf7wubz8g4rbm300i7a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9cf7wubz8g4rbm300i7a.png" alt="alt_text" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 2: Structured concurrency example.&lt;/p&gt;

&lt;p&gt;Furthermore, the lifetime of the subtasks should not be more than the parent itself. Imagine a task operation that would compose results of multiple fast-running I/O operations concurrently if each operation is executed in a thread. The structured concurrency model brings thread programming closer to the ease of single-threaded code style by leveraging the virtual threads API and the &lt;code&gt;StructuredTaskScope&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Response&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;ExecutionException&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;InterruptedException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StructuredTaskScope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ShutdownOnFailure&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Supplier&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  &lt;span class="n"&gt;user&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fork&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;findUser&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="nc"&gt;Supplier&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fork&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fetchOrder&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

        &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;            &lt;span class="c1"&gt;// Join both subtasks&lt;/span&gt;
             &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;throwIfFailed&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// ... and propagate errors&lt;/span&gt;

        &lt;span class="c1"&gt;// Here, both subtasks have succeeded, so compose their results&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Sequenced collections
&lt;/h2&gt;

&lt;p&gt;In&lt;a href="https://openjdk.org/projects/jdk/21/" rel="noopener noreferrer"&gt; JDK 21&lt;/a&gt;, a new set of collection interfaces are introduced to enhance the experience of using collections (Figure 3). For example, if one needs to get a reverse order of elements from a collection, depending on which collection is in use, it can be tedious. There can be inconsistencies retrieving the encounter order depending on which collection is being used; for example, &lt;code&gt;SortedSet&lt;/code&gt; implements one, but &lt;code&gt;HashSet&lt;/code&gt; doesn't, making it cumbersome to achieve this on different data sets.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvxyv4n8m58rtrjnc6rnk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvxyv4n8m58rtrjnc6rnk.png" alt="alt_text" width="800" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 3: Sequenced Collections.&lt;/p&gt;

&lt;p&gt;To fix this, the&lt;a href="https://openjdk.org/jeps/431" rel="noopener noreferrer"&gt; SequencedCollection&lt;/a&gt; interface aids the encounter order by adding a reverse method as well as the ability to get the first and the last elements. Furthermore, there are also &lt;code&gt;SequencedMap&lt;/code&gt; and &lt;code&gt;SequencedSet&lt;/code&gt; interfaces.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;SequencedCollection&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;E&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Collection&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;E&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// new method&lt;/span&gt;
    &lt;span class="nc"&gt;SequencedCollection&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;E&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;reversed&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// methods promoted from Deque&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;addFirst&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;E&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;addLast&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;E&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="no"&gt;E&lt;/span&gt; &lt;span class="nf"&gt;getFirst&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="no"&gt;E&lt;/span&gt; &lt;span class="nf"&gt;getLast&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="no"&gt;E&lt;/span&gt; &lt;span class="nf"&gt;removeFirst&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="no"&gt;E&lt;/span&gt; &lt;span class="nf"&gt;removeLast&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So now, not only is it possible to get the encounter order, but you can also remove and add the first and last elements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Record patterns
&lt;/h2&gt;

&lt;p&gt;Records were introduced as a preview in Java 14, which also gave us Java enums. &lt;code&gt;record&lt;/code&gt; is another special type in Java, and its purpose is to ease the process of developing classes that act as data carriers only.&lt;/p&gt;

&lt;p&gt;In JDK 21,&lt;a href="https://openjdk.org/jeps/440" rel="noopener noreferrer"&gt; record patterns&lt;/a&gt; and type patterns can be nested to enable a declarative and composable form of data navigation and processing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// To create a record:&lt;/span&gt;

&lt;span class="nc"&gt;Public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;Todo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;completed&lt;/span&gt;&lt;span class="o"&gt;){}&lt;/span&gt;

&lt;span class="c1"&gt;// To create an Object:&lt;/span&gt;

&lt;span class="nc"&gt;Todo&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Todo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nc"&gt;Learn&lt;/span&gt; &lt;span class="nc"&gt;Java&lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before JDK 21, the entire record would need to be deconstructed to retrieve accessors.. However, now it is much more simplified to get the values. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;printTodo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nf"&gt;Todo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;completed&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;print&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;print&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;completed&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The other advantage of record patterns is also nested records and accessing them. An example from the JEP definition itself shows the ability to get to the &lt;code&gt;Point&lt;/code&gt; values, which are part of &lt;code&gt;ColoredPoint&lt;/code&gt;, which is nested in a &lt;code&gt;Rectangle&lt;/code&gt;. This makes it way more useful than before, when all the records needed to be deconstructed every time. &lt;/p&gt;

&lt;h2&gt;
  
  
  String templates
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://openjdk.org/jeps/430" rel="noopener noreferrer"&gt;String templates&lt;/a&gt; are a preview feature in JDK 21. However, it attempts to bring more reliability and better experience to &lt;code&gt;String&lt;/code&gt; manipulation to avoid common pitfalls that can sometimes lead to undesirable results, such as injections. Now you can write template expressions and render them out in a &lt;code&gt;String&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// As of Java 21&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Shaaf"&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;greeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Hello \{name}"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;greeting&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, the second line is the expression, and upon invoking, it should render &lt;code&gt;Hello Shaaf&lt;/code&gt;. Furthermore, in cases where there is a chance of illegal Strings for example, SQL statements or HTML that can cause security issues—the template rules only allow escaped quotes and no illegal entities in HTML documents.&lt;/p&gt;

&lt;h2&gt;
  
  
  More JEPs in JDK 21
&lt;/h2&gt;

&lt;p&gt;Other JEPs that I did not cover in this article but in another post&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JEP 439:&lt;a href="https://openjdk.org/jeps/439" rel="noopener noreferrer"&gt; Generational ZGC&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;JEP 441:&lt;a href="https://openjdk.org/jeps/441" rel="noopener noreferrer"&gt; Pattern Matching for switch&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;JEP 442:&lt;a href="https://openjdk.org/jeps/442" rel="noopener noreferrer"&gt; Foreign Function &amp;amp; Memory API (Third Preview)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;JEP 443:&lt;a href="https://openjdk.org/jeps/443" rel="noopener noreferrer"&gt; Unnamed Patterns and Variables (Preview)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;JEP 445:&lt;a href="https://openjdk.org/jeps/445" rel="noopener noreferrer"&gt; Unnamed Classes and Instance Main Methods (Preview)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;JEP 448:&lt;a href="https://openjdk.org/jeps/448" rel="noopener noreferrer"&gt; Vector API (Sixth Incubator)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;JEP 449:&lt;a href="https://openjdk.org/jeps/449" rel="noopener noreferrer"&gt; Deprecate the Windows 32-bit x86 Port for Removal&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;JEP 451:&lt;a href="https://openjdk.org/jeps/451" rel="noopener noreferrer"&gt; Prepare to Disallow the Dynamic Loading of Agents&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;JEP 452:&lt;a href="https://openjdk.org/jeps/452" rel="noopener noreferrer"&gt; Key Encapsulation Mechanism API&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Get support for Java&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Support for OpenJDK and Eclipse Temurin is available to Red Hat customers through a subscription to&lt;a href="https://www.redhat.com/en/products/runtimes" rel="noopener noreferrer"&gt; Red Hat Runtimes&lt;/a&gt;, Red Hat Enterprise Linux, and Red Hat OpenShift. Contact your local Red Hat representative or&lt;a href="https://www.redhat.com/en/about/contact/sales" rel="noopener noreferrer"&gt; Red Hat sales&lt;/a&gt; for more details. You can expect support for Java and other runtimes as described under the&lt;a href="https://access.redhat.com/support/policy/updates/jboss_notes/" rel="noopener noreferrer"&gt; Red Hat Product Update and Support Lifecycle&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Resources&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=rKG6nvk9xlE" rel="noopener noreferrer"&gt;Video: What is Eclipse Temurin?&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://access.redhat.com/documentation/en-us/openjdk/17/html-single/getting_started_with_eclipse_temurin/index" rel="noopener noreferrer"&gt;Getting started with Eclipse Temurin&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.redhat.com/en/blog/red-hat-joins-eclipse-adoptium-working-group" rel="noopener noreferrer"&gt;Red Hat joins the Eclipse Adoptium Working Group&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.redhat.com/en/blog/eclipse-adoptium-achieves-its-first-java-se-release" rel="noopener noreferrer"&gt;Eclipse Adoptium achieves its first Java SE release&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.redhat.com/en/about/press-releases/red-hat-introduces-commercial-support-openjdk-microsoft-windows" rel="noopener noreferrer"&gt;Red Hat Introduces Commercial Support for OpenJDK on Microsoft Windows&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.redhat.com/en/blog/history-and-future-openjdk" rel="noopener noreferrer"&gt;The history and future of OpenJDK&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>java</category>
      <category>opensouce</category>
      <category>programming</category>
    </item>
    <item>
      <title>Keycloak Operator for Kubernetes - a Basic Tutorial</title>
      <dc:creator>Shaaf Syed</dc:creator>
      <pubDate>Sat, 09 Sep 2023 05:18:41 +0000</pubDate>
      <link>https://forem.com/sshaaf/keycloak-operator-for-kubernetes-a-basic-tutorial-1902</link>
      <guid>https://forem.com/sshaaf/keycloak-operator-for-kubernetes-a-basic-tutorial-1902</guid>
      <description>&lt;p&gt;The Keycloak team &lt;a href="https://www.keycloak.org/2022/09/operator-crs.html" rel="noopener noreferrer"&gt;announced&lt;/a&gt; that they were going to move to a new Operator framework that will effictevely manage Keycloak installatons on a Kubernetes cluster. &lt;br&gt;
So what is an &lt;a href="https://operatorframework.io/what/" rel="noopener noreferrer"&gt;Operator&lt;/a&gt; in the Kubernetes context. Simplyfying it a bit here... Its basically a component that takes over the operational aspects of your application. So rather then managing all of the lifecycle and state in scripts and in our minds in someways is coded into an operator. For us Keycloak Operator does exactly that and in this blog I will cover how to setup a very simple Keycloak installation and get ready for development. If you are interested in knowing more about Operators you will likely find some good info &lt;a href="https://operatorframework.io/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you arent familiar with &lt;a href="https://keycloak.org" rel="noopener noreferrer"&gt;Keycloak&lt;/a&gt;; its an opensource identity and access management software. Current version is 22, its used alot already in the wild. It provides single sign on capabitlity with OAuth/OIDC, AD, LDAP and SAML v2 as well. If you aren't very familiar with Keycloak, I have also written a small self-paced &lt;a href="https://shaaf.dev/keycloak-tutorial" rel="noopener noreferrer"&gt;Keycloak tutorial&lt;/a&gt; that goes through all the basics and some advance configs too. &lt;/p&gt;

&lt;p&gt;Let's get cracking on installing a basic keycloak instance backed by a PostgreSQL database. &lt;/p&gt;

&lt;p&gt;So heres the plan&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install Operator on &lt;a href="https://www.redhat.com/en/technologies/cloud-computing/openshift" rel="noopener noreferrer"&gt;OpenShift&lt;/a&gt; cluster (a distribution of Kubernetes by Red Hat)&lt;/li&gt;
&lt;li&gt;Install database for the Keycloak backend.&lt;/li&gt;
&lt;li&gt;Create SSL certificate for use with keycloak backend.&lt;/li&gt;
&lt;li&gt;Install the first Keycloak instance. &lt;/li&gt;
&lt;li&gt;Importing a realm &lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Installing the Operator
&lt;/h3&gt;

&lt;p&gt;To install the Keycloak operator I am choosing to install it via the webconsole which is an easy way to do it. Its also possible to do it via CLI. &lt;/p&gt;

&lt;p&gt;Simply search for &lt;code&gt;keycloak&lt;/code&gt; and press install. e.g. &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2F2023%2F09%2F08%2Finstall-keycloak-operator-1.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2F2023%2F09%2F08%2Finstall-keycloak-operator-1.jpeg" title="OpenShift Operator Install" alt="alt_text" width="800" height="286"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you press install the installer will ask which namespace you would like to install to. In my case it the &lt;code&gt;rhbk&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2F2023%2F09%2F08%2Finstall-keycloak-operator-2.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2F2023%2F09%2F08%2Finstall-keycloak-operator-2.jpeg" title="OpenShift Operator Install" alt="alt_text" width="800" height="731"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This could take a couple of seconds or minutes depending on your cluster. &lt;/p&gt;
&lt;h3&gt;
  
  
  Installing the database
&lt;/h3&gt;

&lt;p&gt;For the Keycloak server to store its state and data it should have a database. For this I will use Crunchy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;StatefulSet&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgresql-db&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;serviceName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgresql-db-service&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgresql-db&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgresql-db&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgresql-db&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:latest&lt;/span&gt;
          &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/data&lt;/span&gt;
              &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cache-volume&lt;/span&gt;
          &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_PASSWORD&lt;/span&gt;
              &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;testpassword&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PGDATA&lt;/span&gt;
              &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/data/pgdata&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_DB&lt;/span&gt;
              &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keycloak&lt;/span&gt;
      &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cache-volume&lt;/span&gt;
          &lt;span class="na"&gt;emptyDir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres-db&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgresql-db&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LoadBalancer&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5432&lt;/span&gt;
    &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5432&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create SSL certificate for use with keycloak backend.
&lt;/h3&gt;

&lt;p&gt;Okay time to create a self-signed certificate, incase it was production assuming that would be via a certificate autority instead. But here since I am just testing this out we an just use the self-signed certificate. On a linux machine I can create a self-signed certificate like this where &lt;code&gt;keycloak.rhbk.apps.green.demoshift.com&lt;/code&gt; is the address to my keyclaok instance and &lt;code&gt;O&lt;/code&gt; is &lt;code&gt;Test demoshift&lt;/code&gt;. it could be anything you want to denote here in my case its a test Org.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl req &lt;span class="nt"&gt;-subj&lt;/span&gt; &lt;span class="s1"&gt;'/CN=keycloak.rhbk.apps.green.demoshift.com/O=Test demoshift./C=US'&lt;/span&gt; &lt;span class="nt"&gt;-newkey&lt;/span&gt; rsa:2048 &lt;span class="nt"&gt;-nodes&lt;/span&gt; &lt;span class="nt"&gt;-keyout&lt;/span&gt; key.pem &lt;span class="nt"&gt;-x509&lt;/span&gt; &lt;span class="nt"&gt;-days&lt;/span&gt; 365 &lt;span class="nt"&gt;-out&lt;/span&gt; certificate.pem

Generating a 2048 bit RSA private key
.....................+++++
.....................+++++
writing new private key to &lt;span class="s1"&gt;'key.pem'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets load this certificate into our Kubernetes environment by usng the following command. Make sure the path for the cert is correct, in my case I am in the same dir as my cert.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create secret tls example-tls-secret &lt;span class="nt"&gt;--cert&lt;/span&gt; certificate.pem &lt;span class="nt"&gt;--key&lt;/span&gt; key.pem 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also a good practise is to load the database password as a secret rather then it littering around different yaml files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create secret generic keycloak-db-secret &lt;span class="se"&gt;\ &lt;/span&gt;
            &lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgres &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;testpassword
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install the first Keycloak instance
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;k8s.keycloak.org/v2alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Keycloak&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example-kc&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;instances&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;vendor&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
    &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres-db&lt;/span&gt;
    &lt;span class="na"&gt;usernameSecret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keycloak-db-secret&lt;/span&gt;
      &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;username&lt;/span&gt;
    &lt;span class="na"&gt;passwordSecret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keycloak-db-secret&lt;/span&gt;
      &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;password&lt;/span&gt;
  &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;tlsSecret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example-tls-secret&lt;/span&gt;
  &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keycloak.rhbk.apps.green.demoshift.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if we go back to the webconsole you should be able to see.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1. Keycloak operator pod&lt;/li&gt;
&lt;li&gt;2. PG database pod&lt;/li&gt;
&lt;li&gt;3. Keycloak server pod. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;OpenShift has this nice feature that you can see the route once you click your application. also visible in the image below. Head over to your instance and login. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2F2023%2F09%2F08%2Fkeycloak-kubernetes-server-install.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2F2023%2F09%2F08%2Fkeycloak-kubernetes-server-install.jpeg" title="Keycloak server Install" alt="alt_text" width="800" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To login obviously one needs the admin password which in this case has been autogenerated by the operator. Lets get that by running the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get secret example-kc-initial-admin &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.data.password}'&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;--decode&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Assuming we have the password, lets go back to the route and click on &lt;code&gt;Adminstration console&lt;/code&gt;. Username is &lt;code&gt;admin&lt;/code&gt; and password should be what we retrieved from the command above. &lt;/p&gt;

&lt;p&gt;Login and &lt;code&gt;Viola!&lt;/code&gt; we have landed on our freshly installed Keycloak server. &lt;/p&gt;

&lt;h3&gt;
  
  
  Importing a realm
&lt;/h3&gt;

&lt;p&gt;At this moment we only have the master realm. Typically one should leave the master realm intact without much changes. The logic behind that would be not to lock ourselves out. How about we import a realm. And here the power of Operator comes into play. We will create a RealmImportCR and that will add a new realm to Keycloak. Imagine if the Keycloak pod goes down at some point, the operator will ensure that its brought back to the same state it was and where is that state going to be? in the CRs and the database.&lt;/p&gt;

&lt;p&gt;Okay lets test this out further, how about adding a new realm via the KCImportRealmCR&lt;/p&gt;

&lt;p&gt;Let's go back to the Operator view&lt;/p&gt;

&lt;p&gt;Click on KeycloakRealmImport -&amp;gt; &lt;code&gt;Create instance&lt;/code&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2F2023%2F09%2F08%2FKCRealmImport.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2F2023%2F09%2F08%2FKCRealmImport.jpeg" title="Keycloak server realm import" alt="alt_text" width="800" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I had a &lt;a href="https://github.com/sshaaf/book-service/blob/main/src/main/resources/quarkus-realm.json" rel="noopener noreferrer"&gt;realm json&lt;/a&gt; file. And obviously operators and most of the stuff in K8s expects yaml so I had to make a conversion. I used the following &lt;a href="https://github.com/mikefarah/yq/" rel="noopener noreferrer"&gt;yq&lt;/a&gt; for conversion. There are also online tools available, but given the sensitive nature of realms I wouldnt suggest using a random online tool to convert to yaml. &lt;/p&gt;

&lt;p&gt;So here is the resultant &lt;a href="https://gist.github.com/sshaaf/7b5a0fc6c81289440cb797e049b99472" rel="noopener noreferrer"&gt;CR&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Load the yaml and press create&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2F2023%2F09%2F08%2FCR-realm-import.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2F2023%2F09%2F08%2FCR-realm-import.jpeg" title="Keycloak server realm import" alt="alt_text" width="800" height="740"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It can take a couple of seconds to that it will come up. As soon as we create the CR, the Keycloak Operator will pick it up and add it to the running Keycloak instance. If you log back into the admin console it should look like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2F2023%2F09%2F08%2Frealm-imported.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2F2023%2F09%2F08%2Frealm-imported.jpeg" title="Keycloak server realm import" alt="alt_text" width="784" height="670"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And if we explore further and look into the clients there is a &lt;code&gt;backend-service&lt;/code&gt;. Its a Client config for a Quarkus based REST service written in Java. Thats for another post. There is alot more that can be done with Operators. &lt;/p&gt;

&lt;p&gt;If you are looking for more in depth details the Keycloak &lt;a href="https://www.keycloak.org/documentation" rel="noopener noreferrer"&gt;docs&lt;/a&gt; is a great resource. If you are looking to explore more and try this installation you can also use the following &lt;a href="https://shaaf.dev/keycloak-tutorial" rel="noopener noreferrer"&gt;Keycloak tutorial&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;More to come on Keycloak in later posts.&lt;/p&gt;

</description>
      <category>keycloak</category>
      <category>operations</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>How-to configure your first user with OpenShift IDP - htpasswd</title>
      <dc:creator>Shaaf Syed</dc:creator>
      <pubDate>Mon, 21 Aug 2023 13:22:40 +0000</pubDate>
      <link>https://forem.com/sshaaf/how-to-configure-your-first-user-with-openshift-idp-htpasswd-jhe</link>
      <guid>https://forem.com/sshaaf/how-to-configure-your-first-user-with-openshift-idp-htpasswd-jhe</guid>
      <description>&lt;p&gt;There are multiple options to configure OpenShift integration with an IDP. Usually one would use something like an LDAP, AD (Active Directory) for use in a production cluster or a corporate environment. This guide is a basic how-to in configuring using the htpasswd file which is one of the IDP integration options in Openshift 4.x. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;htpasswd&lt;/strong&gt; is a tool used to create and update the flat-files used to store usernames and password for basic authentication of HTTP users. These flat-files are commonly used by the Apache HTTP Server, as well as some other server software, to provide password protection for web content.&lt;/p&gt;

&lt;p&gt;Okay lets do this. &lt;/p&gt;

&lt;p&gt;You want to run the following command on your linux/unix box. This command should create a new .htpasswd file (because of -c), use bcrypt encryption for the password (because of -B), and take the password directly from the command line (because of -b).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;htpasswd -c -B -b myhttpasswdfile.htpasswd mydesireduser mypassword
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;httpasswd&lt;/code&gt; - the cli to generate the file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-c&lt;/code&gt; - This option is used to create a new password file. If the specified password file already exists, using this option will overwrite it. Be cautious when using -c to ensure you don't unintentionally erase existing files.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;B&lt;/code&gt; - This option tells htpasswd to use the bcrypt encryption for the password. Bcrypt is a strong method for hashing passwords, and it's recommended over the older methods like MD5 or SHA because of its resistance to brute-force attacks.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;b&lt;/code&gt; - This option allows you to specify the password on the command line rather than being prompted for it. This can be helpful in scripting but is considered less secure because the password can be viewed in the command history or by other users using commands like ps on UNIX-based systems.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Okay now that we have a simple htpasswd file with one user. Lets add it to OpenShift. &lt;/p&gt;

&lt;p&gt;Login to your OpenShift Console and hit this URL:\&lt;br&gt;
 &lt;code&gt;&amp;lt;Your OpenShift Console&amp;gt;/k8s/cluster/config.openshift.io~v1~OAuth/cluster&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Or Goto &lt;code&gt;Adminsitration &amp;gt; Cluster Settings &amp;gt;&lt;/code&gt; Tab "Configuration" and Select OAuth. like the following screenshot&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2F2023%2F08%2F17%2FOpenShift-ClusterSettings-Config-OAuth.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2F2023%2F08%2F17%2FOpenShift-ClusterSettings-Config-OAuth.png" title="OpenShift Cluster Settings, Config OAuth" alt="alt_text" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Okay perfect once at the OAuth Config screen, click add under &lt;code&gt;Identity providers&lt;/code&gt;, you should be able to see the dropdown list as follows. Select htpasswd from it. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2F2023%2F08%2F17%2FOpenShift-OAuth-htpasswd-select.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2F2023%2F08%2F17%2FOpenShift-OAuth-htpasswd-select.png" title="OpenShift OAuth types" alt="alt_text" width="400" height="547"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And then lets just browse to the htpasswd file created and press add. This will take a couple of secs and should be ready as the CR is submitted. What OpenShift will do in the backgroud is create the htpasswd OAuth provider and it will sync to all the master nodes under &lt;code&gt;/etc/origin/master/&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2F2023%2F08%2F17%2FOpenShift-OAuth-htpasswd-config.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2F2023%2F08%2F17%2FOpenShift-OAuth-htpasswd-config.png" title="OpenShift Config" alt="alt_text" width="800" height="584"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should now be able to login via &lt;code&gt;oc login&lt;/code&gt; or via the OpenShift console. &lt;br&gt;
Incase of any issues check the tips &lt;a href="https://access.redhat.com/solutions/4110561" rel="noopener noreferrer"&gt;here for diagnostics&lt;/a&gt; or &lt;a href="https://docs.openshift.com/container-platform/4.13/authentication/identity_providers/configuring-htpasswd-identity-provider.html" rel="noopener noreferrer"&gt;docs&lt;/a&gt;&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>htpasswd</category>
      <category>configuration</category>
      <category>identity</category>
    </item>
    <item>
      <title>Processing images in Java with OpenCV and Quarkus</title>
      <dc:creator>Shaaf Syed</dc:creator>
      <pubDate>Wed, 13 Apr 2022 10:42:36 +0000</pubDate>
      <link>https://forem.com/sshaaf/processing-images-in-java-with-opencv-and-quarkus-1d7k</link>
      <guid>https://forem.com/sshaaf/processing-images-in-java-with-opencv-and-quarkus-1d7k</guid>
      <description>&lt;p&gt;If you are into Computer vision, you probably are familiar with &lt;a href="https://opencv.org/" rel="noopener noreferrer"&gt;OpenCV&lt;/a&gt;. Its an amazing library that has almost everything one needs to do 2D and 3D processing and much more. Gesture recognition, face detection, Motion tracking, think of anything related to image processing and OpenCV can be your goto. Its based on the BSD license, so you can just download it and start using it. &lt;/p&gt;

&lt;p&gt;OpenCV is written in C, and there are nice Java bindings for it too. If you are a Java developer like me and dont want to get into all the loading and building native bindings etc., then read on. In this article I will show how you can use OpenCV with the popular new framework Quarkus, without worrying about installing libraries or reloading the entire app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://quarkus.io/" rel="noopener noreferrer"&gt;Quarkus&lt;/a&gt; is a container first, Kubernetes native framework that was started by Red Hat and it focuses purely on &lt;a href="https://quarkus.io/developer-joy/" rel="noopener noreferrer"&gt;Developer Joy&lt;/a&gt;. The fun part IMHO is the live coding feature, which we will see in our app. I dont really need to keep on building and reloading my app, even though it uses JNI, and I can just simply keep chugging along and coding with ease. I just love this! It makes life simpler. &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Setting up&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Assuming you have a working Java environment. that would mean a Java runtime, a Maven or Gradle system for build. You might be thinking that you need to download the OpenCV libraries for JNI to work etc.. well guess what you dont need to! Quarkus OpenCV extension will take care of it. Quarkus extensions are a way of enabling more functionality to the core Quarkus framework. Quarkiverse is the hub that holds all extensions. So if you are e.g. looking for a nice framework to generate docs, or fuzzy search with hibernate-search or Amazon services integration etc etc.. Well take a look for yourself &lt;a href="https://github.com/quarkiverse" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Okay so we are going to use the Quarkus-OpenCV &lt;a href="https://github.com/quarkiverse/quarkus-opencv" rel="noopener noreferrer"&gt;extension&lt;/a&gt;.&lt;br&gt;
To do this we will first create the a simple Java cli project from code.quarkus.io&lt;/p&gt;

&lt;p&gt;There's a couple of ways to get started. One could just head over to code.quarkus.io and create a project there or run local maven command or use the Quarkus cli. I am going to take the approach that I am most well known too. Lets start with maven.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;1&amp;gt; mvn io.quarkus.platform:quarkus-maven-plugin:2.7.5.Final:create \
                    -DprojectGroupId=org.acme \
                    -DprojectArtifactId=getting-started \
                    -Dextensions="resteasy"

&amp;lt;2&amp;gt; cd getting-started                    

&amp;lt;3&amp;gt; ./mvnw quarkus:add-extension -Dextensions="io.quarkiverse.opencv:quarkus-opencv"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&amp;lt;1&amp;gt; - here we download the starter project from code.quarkus.io. The project name is &lt;code&gt;getting-started&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&amp;lt;2&amp;gt; - I am changing directory into the newly created project.&lt;/li&gt;
&lt;li&gt;&amp;lt;3&amp;gt; - And finally I am also adding the OpenCV extension from Quarkiverse to our project. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Perfect now we should have a project to get to work. Lets open it up in an IDE. Then&lt;/p&gt;

&lt;p&gt;Lets check the pom.xml that it has the right dependencies. If you are following this along, make sure the following is the list of dependencies in the pom.xml.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  &amp;lt;dependencies&amp;gt;
    &amp;lt;dependency&amp;gt;
      &amp;lt;groupId&amp;gt;io.quarkus&amp;lt;/groupId&amp;gt;
      &amp;lt;artifactId&amp;gt;quarkus-arc&amp;lt;/artifactId&amp;gt;
    &amp;lt;/dependency&amp;gt;
    &amp;lt;dependency&amp;gt;
      &amp;lt;groupId&amp;gt;io.quarkiverse.opencv&amp;lt;/groupId&amp;gt;
      &amp;lt;artifactId&amp;gt;quarkus-opencv&amp;lt;/artifactId&amp;gt;
      &amp;lt;version&amp;gt;LATEST&amp;lt;/version&amp;gt;
    &amp;lt;/dependency&amp;gt;
    &amp;lt;dependency&amp;gt;
      &amp;lt;groupId&amp;gt;io.quarkus&amp;lt;/groupId&amp;gt;
      &amp;lt;artifactId&amp;gt;quarkus-junit5&amp;lt;/artifactId&amp;gt;
      &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
    &amp;lt;/dependency&amp;gt;
    &amp;lt;dependency&amp;gt;
      &amp;lt;groupId&amp;gt;io.rest-assured&amp;lt;/groupId&amp;gt;
      &amp;lt;artifactId&amp;gt;rest-assured&amp;lt;/artifactId&amp;gt;
      &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
    &amp;lt;/dependency&amp;gt;
  &amp;lt;/dependencies&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, since I have removed the rest-easy dependency, I shall also remove the REST end point. in &lt;code&gt;org.acme.GreetingResource.java&lt;/code&gt; and the Tests thereof.&lt;/p&gt;

&lt;p&gt;Perfect so now we have a clean project to get started. Here&lt;/p&gt;

&lt;h3&gt;
  
  
  Create the CLI Main
&lt;/h3&gt;

&lt;p&gt;Quarkus provides the option to create command line apps, and this is so kool, since you can also compile these into native. We are just going to make simple app for demo purpose in this blog. But if you are looking into creating some serious nirvana of cli apps take a look at &lt;a href="https://quarkus.io/guides/picocli" rel="noopener noreferrer"&gt;this guide&lt;/a&gt; at quarkus.io using picocli or &lt;a href="https://jbang.dev" rel="noopener noreferrer"&gt;JBang&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;In my app I try to keep it simple. Lets create the barebones QMain Class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package org.acme;

import io.quarkus.runtime.QuarkusApplication;
import io.quarkus.runtime.annotations.QuarkusMain;

@QuarkusMain
public class QMain implements QuarkusApplication {

    @Override
    public int run(String... args) throws Exception {
        // TODO Auto-generated method stub
        return 0;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I am now going to run this app in a terminal. And the command should initiate a livecoding session as the app is started. &lt;/p&gt;

&lt;h3&gt;
  
  
  Initiate Live Coding
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/demos/getting-started mvn quarkus:dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2Fquarkus-livecode.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2Fquarkus-livecode.png" title="Quarkus started" alt="alt_text" width="800" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;So what's live coding?&lt;/em&gt; Well just keep on coding, dont worry about restarting your app, or adding new dependencies and config or rebuidling.. etc. You just dont need to restart the app everytime! (*almost)&lt;/p&gt;

&lt;p&gt;I am going to leave the terminal running and get back to coding in QMain.&lt;/p&gt;

&lt;p&gt;I am going to add two class level properties, one to read an image from and the second to save an image to. Quarkus via the Microprofile APIs provdies the &lt;code&gt;@ConfigProperty&lt;/code&gt;. This enables me to inject config properties into my app. Here is how I do it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    // Set these in your application.properties
    @ConfigProperty(name = "cli.sourceImagePath")
    String testImage;

    // Set these in your application.properties
    @ConfigProperty(name = "cli.targetImagePath")
    String targetImage;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in my application.properties I add the path to these files&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cli.sourceImagePath=images/testImage.jpg
cli.targetImagePath=images/testImageDetected-output.jpg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One is the &lt;code&gt;cli.sourceImagePath&lt;/code&gt;, this has to be present in order for it to be processed. And the &lt;code&gt;cli.targetImagePath&lt;/code&gt; doesnt need to be present since I will save the image there. In my case I have added the images into the root of my project, hence the path &lt;code&gt;images/&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Load and Save images
&lt;/h3&gt;

&lt;p&gt;Now back to QMain. I am going to add the save and load methods for these paths.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    /**
     * Loading the image
     */
    public Mat loadImage(String imagePath) {
        Imgcodecs imageCodecs = new Imgcodecs();
        return imageCodecs.imread(imagePath);
    }



    /**
     * Save the image to the targetPath
     */
    public void saveImage(Mat imageMatrix, String targetPath) {
        Imgcodecs imgcodecs = new Imgcodecs();
        imgcodecs.imwrite(targetPath, imageMatrix);
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mat has two parts the Image headers, and the data matrix.The header is constant but the size of the matrix will depend on the image. Mat is used to load the images from a path and Mat forms the basic construct for us to perform operations on the image. &lt;/p&gt;

&lt;h3&gt;
  
  
  Defining the Filter Interface
&lt;/h3&gt;

&lt;p&gt;And to make multiple Filters to process images, I am going to define an interface that takes Mat as a source, processes it and then returns Mat. This way I can apply multiple filters on an image before I save them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package org.acme;

import org.opencv.core.Mat;

public interface Filter {

    public Mat process(Mat src);

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Connecting the dots
&lt;/h3&gt;

&lt;p&gt;And finally lets try to connect the structure by adding the methods calls to the QMain's run method.And spark some joy!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    @Override
    public int run(String... args) throws Exception {
        Mat m = loadImage(testImage);

        saveImage(m, targetImage);
        return 0;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple, all I do above is load the image and save the image. No processing yet. How do you test that. Remember the terminal where we ran &lt;code&gt;mvn quarkus:dev&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Press space on that. And the program will just continue executing. How kool is that! No need to rebuild, all the config, the new classes everything just worked. Thats what sparks the &lt;code&gt;Developer Joy!&lt;/code&gt; , with Quarkus! &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2Fquarkus-livecode-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2Fquarkus-livecode-2.png" title="Quarkus started" alt="alt_text" width="800" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Checkout the logs. It states &lt;code&gt;File change detected&lt;/code&gt; for the application.properties, more over It also lists the Classes that have changed. &lt;/p&gt;

&lt;p&gt;So what just happened? &lt;br&gt;
Quarkus runtime just executed the cli app. which means that if I goto &lt;code&gt;cli.targetImagePath&lt;/code&gt; I should see an image created there. At this point it will look the same as the source image, since I havent processed anything on it yet. Guess what? thats exactly whatI plan to do now. &lt;/p&gt;

&lt;p&gt;Moving on, the image I am using today is provided by &lt;a href="https://unsplash.com/@shuttergames?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Shuttergames&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/pakistan?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2FtestImage.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2FtestImage.jpg" title="Quarkus Base Image" alt="alt_text" width="800" height="574"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Gaussian Blur
&lt;/h3&gt;

&lt;p&gt;The first filter I create is a &lt;code&gt;GaussianBlur&lt;/code&gt;filter. Its a &lt;a href="https://en.wikipedia.org/wiki/Low-pass_filter" rel="noopener noreferrer"&gt;low-pass filter&lt;/a&gt; meaning that it attenuates higher frequency signals. The overall visual effect would be a smooth blur.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package org.acme;



import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;

public class GaussianBlur implements Filter {

    @Override
    public Mat process(Mat src) {

        if(src != null) {
            int MAX_KERNEL_LENGTH = 45;
            Mat dst = new Mat();
            for (int i = 1; i &amp;lt; MAX_KERNEL_LENGTH; i = i + 2) {
                Imgproc.GaussianBlur(src, dst, new Size(i, i), 0, 0);
            }
            return dst;
        }

        else throw new IllegalArgumentException("Looking for Mat nothing found!, try passing org.opencv.core.Mat to process");
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above we define a kernel size. Kernel is a matrix that defines how an anchored pixel and its surrounding pixels are changed based on the function provided. &lt;/p&gt;

&lt;p&gt;
    &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2Fopencv-kernel.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2Fopencv-kernel.png" width="200" height="208"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;The kernels will define the size of the convolution, the weights, and an anchor point positioned at the center. The process method takes a Mat object applies the GuassianBlur filter with the Kernel size, and finally returns the Mat object back. &lt;/p&gt;

&lt;h3&gt;
  
  
  RGB2Grey
&lt;/h3&gt;

&lt;p&gt;Adding the following to the run mehtod in QMain so I can call my Filter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    @Override
    public int run(String... args) throws Exception {
        Mat m = loadImage(testImage);

        m = new GaussianBlur().process(m);

        saveImage(m, targetImage);
        return 0;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perfect, now if I press the space bar again on the terminal, its should execute all my changes. Resulting in the blur as shown below. The blur is smooth, almost like if its been overlayed with a lense. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2FGaussianBlur.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2FGaussianBlur.png" title="Quarkus Gaussian Blur" alt="alt_text" width="800" height="553"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, lets make another filter. this time how about I make the image Grey. To do that OpenCV already provides a simple function. Again I am using the Fitler interface for this hence passing a Mat and recieving one back.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package org.acme;

import org.opencv.core.Mat;
import org.opencv.imgproc.Imgproc;



public class RGB2Grey implements Filter{

    @Override
    public Mat process(Mat src) {
        if(src != null) {
            Mat dst = src;
            Imgproc.cvtColor(src, dst, Imgproc.COLOR_RGB2GRAY);
            return dst;
        }
        else throw new IllegalArgumentException("Looking for Mat nothing found!, try passing org.opencv.core.Mat to process");
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above code using &lt;code&gt;ImgProc&lt;/code&gt; that provides this operation to move all pixels from RGB to Grey.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; m = new RGB2Grey().process(m);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adding the above to my run method and pressing the space bar again in the terminal should execute all my cahnges again. and Vola! I have a Grey tone image.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2FRGB2Grey.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshaaf.dev%2Fimages%2FRGB2Grey.png" title="Quarkus RGB2Grey" alt="alt_text" width="800" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hope you enjoyed this run through on how to use Quarkus and OpenCV together. For more examples take a look at the &lt;a href="https://github.com/sshaaf/quarkus-opencv-examples" rel="noopener noreferrer"&gt;github repo with code examples&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>java</category>
      <category>opencv</category>
      <category>quarkus</category>
      <category>programming</category>
    </item>
    <item>
      <title>SQL cache stores and more in Data Grid 8.3</title>
      <dc:creator>Shaaf Syed</dc:creator>
      <pubDate>Thu, 24 Feb 2022 15:42:03 +0000</pubDate>
      <link>https://forem.com/sshaaf/sql-cache-stores-and-more-in-data-grid-83-32hd</link>
      <guid>https://forem.com/sshaaf/sql-cache-stores-and-more-in-data-grid-83-32hd</guid>
      <description>&lt;p&gt;&lt;a href="https://developers.redhat.com/products/datagrid/overview" rel="noopener noreferrer"&gt;Red Hat Data Grid&lt;/a&gt; is a distributed, cloud-based datastore offering very fast response times as an in-memory database. The latest version, Data Grid 8.3, features cross-site replication with more observability and two new types of SQL cache store for scaling applications with large datasets. This version also brings improved security, support for Helm charts, and a better command-line interface (CLI).&lt;/p&gt;

&lt;p&gt;This article is an overview of new features and enhancements in this latest version of Red Hat Data Grid.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Cross-site replication with more observability&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;With the latest Data Grid release, you can track cross-site replication operations for each of the backup locations and their caches, including response times and the number of RELAY messages exchanged, as shown in Figure 1.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdevelopers.redhat.com%2Fsites%2Fdefault%2Ffiles%2Frepl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdevelopers.redhat.com%2Fsites%2Fdefault%2Ffiles%2Frepl.png" title="image_tooltip" alt="alt_text" width="800" height="324"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 1. Cross-site replication involves messages at many different levels.&lt;/p&gt;

&lt;p&gt;We have also enabled access to the data via the CLI, the REST API, and Java Management Extensions (JMX) to offer a better user experience to cluster operators. Cross-site replication is also more transparent and observable due to dedicated, operator-managed pods for routing cross-site replication requests.&lt;/p&gt;

&lt;p&gt;Data Grid operators can also configure the number of relay nodes for cross-site replication. This flexibility enhances the cache's scalability and performance over multiple sites.&lt;/p&gt;

&lt;p&gt;Finally, security is of paramount importance for a cross-site cluster, so we have enabled TLS security for router-based cross-site replication. (Scroll down for more about security enhancements in a later section.)&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Scaling with SQL cache stores&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;What do you do if you have a lot of data in a database and want to load the data into the cache? Database schemas can be complex and users might want to define how that data is queried through operations such as SELECT, INSERT, and so on. Perhaps you would also like to support write-through and write-back operations. All of this makes for a complex scenario.&lt;/p&gt;

&lt;p&gt;In this release, we have added two types of SQL cache store:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Table cache store: Loads all data from a single table. Only the table name is required.&lt;/li&gt;
&lt;li&gt;Query cache store: Loads data based on SQL queries.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Useful things you can do with the new SQL cache stores include the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pre-load data from an existing database.&lt;/li&gt;
&lt;li&gt;Expose cache data with a user-defined schema.&lt;/li&gt;
&lt;li&gt;Allow read and write operations.&lt;/li&gt;
&lt;li&gt;Configure the cache store as read-only, and act as a cache loader.&lt;/li&gt;
&lt;li&gt;Configure the cache store to load values on startup.&lt;/li&gt;
&lt;li&gt;Use the cache store with composite keys and values through the &lt;a href="https://developers.google.com/protocol-buffers" rel="noopener noreferrer"&gt;protocol buffers (protobuf) schema&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;With the query cache store, use arbitrary select, select all, delete, delete all, and upsert operations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To set up a cache store, you simply need to drop the database drivers into the server, which can be done with the &lt;a href="https://developers.redhat.com/articles/2022/02/24/sql-cache-stores-and-more-data-grid-83" rel="noopener noreferrer"&gt;Operator&lt;/a&gt; custom resource (CR) on &lt;a href="https://developers.redhat.com/openshift" rel="noopener noreferrer"&gt;Red Hat OpenShift&lt;/a&gt;. After that, users should be able to create SQL cache stores via YAML, JSON, or XML—also a new configuration feature. An &lt;a href="https://github.com/redhat-mw-demos/infinispan-sqlstore-demo" rel="noopener noreferrer"&gt;example using Infinispan and the Quarkus Java framework&lt;/a&gt; is available on GitHub.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;More security enhancements&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Data Grid now provides full support for TLS version 1.3 with OpenSSL native acceleration. We have also increased the flexibility and convenience of security in Data Grid 8.3.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Multiple realms&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;You can combine multiple security realms into a single realm. When authenticating users, Data Grid Server checks each security realm in turn until it finds one that can perform the authentication.&lt;/p&gt;

&lt;p&gt;The following example security realm includes an LDAP realm and a property realm, along with the &lt;code&gt;distributed-realm&lt;/code&gt; element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;security-realms&amp;gt;
   &amp;lt;security-realm name="my-distributed-realm"&amp;gt;
      &amp;lt;ldap-realm&amp;gt;
         &amp;lt;!-- LDAP realm configuration. --&amp;gt;
      &amp;lt;/ldap-realm&amp;gt;
      &amp;lt;properties-realm&amp;gt;
         &amp;lt;!-- Property realm configuration. --&amp;gt;
      &amp;lt;/properties-realm&amp;gt;
      &amp;lt;distributed-realm/&amp;gt;
   &amp;lt;/security-realm&amp;gt;
&amp;lt;/security-realms&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Multiple endpoints&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Users can now also configure multiple endpoints and define separate security realms for them. This enhancement enables more flexible and secure use.&lt;/p&gt;

&lt;p&gt;The following example contains two different endpoint configurations. One endpoint binds to a &lt;code&gt;public&lt;/code&gt; socket, uses an &lt;code&gt;application&lt;/code&gt; security realm, and disables administrative features. Another endpoint binds to a &lt;code&gt;private&lt;/code&gt; socket, uses a &lt;code&gt;management&lt;/code&gt; security realm, and enables administrative features:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;endpoints&amp;gt;
  &amp;lt;endpoint socket-binding="public"
            security-realm="application"
            admin="false"&amp;gt;
    &amp;lt;hotrod-connector/&amp;gt;
    &amp;lt;rest-connector/&amp;gt;
  &amp;lt;/endpoint&amp;gt;
  &amp;lt;endpoint socket-binding="private"
            security-realm="management"&amp;gt;
    &amp;lt;hotrod-connector/&amp;gt;
    &amp;lt;rest-connector/&amp;gt;
  &amp;lt;/endpoint&amp;gt;
&amp;lt;/endpoints&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;PEM files&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Users can now add PEM files directly to their Data Grid Server configuration and use them as trust stores and keystores in a TLS server identity.&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Note: See the &lt;a href="https://access.redhat.com/documentation/en-us/red_hat_data_grid/8.3/html-single/data_grid_security_guide/" rel="noopener noreferrer"&gt;Data Grid Security Guide&lt;/a&gt; and &lt;a href="https://access.redhat.com/documentation/en-us/red_hat_data_grid/8.3/html-single/data_grid_server_guide#distributed-security-realms_security-realms" rel="noopener noreferrer"&gt;Distributed security realms&lt;/a&gt; to learn more about new security features in Data Grid 8.3.&lt;br&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;strong&gt;Deploy Data Grid with Helm charts&lt;/strong&gt;&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;Developers who use &lt;a href="https://helm.sh/" rel="noopener noreferrer"&gt;Helm charts&lt;/a&gt; for application deployment can now use this convenient mechanism to install Data Grid. You can use charts to deploy Data Grid instances, configure clusters, add authentication and authorization, add network access via routes and node ports, enable load balancers, and more, using the Data Grid portal (Figure 2) or the CLI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdevelopers.redhat.com%2Fsites%2Fdefault%2Ffiles%2Fstyles%2Farticle_full_width_1440px_w%2Fpublic%2Fhelm_0.png%3Fitok%3D1XGArnkV" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdevelopers.redhat.com%2Fsites%2Fdefault%2Ffiles%2Fstyles%2Farticle_full_width_1440px_w%2Fpublic%2Fhelm_0.png%3Fitok%3D1XGArnkV" title="image_tooltip" alt="alt_text" width="528" height="214"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 2. Helm charts can be invoked through the Data Grid graphical interface.&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&lt;strong&gt;Note:&lt;/strong&gt; Using Helm charts is intended for sites where the Data Grid Operator is not available and operators must configure, deploy, and manage the cluster manually.&lt;br&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;strong&gt;CLI improvements&lt;/strong&gt;&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;Data Grid 8.3 has a few updates for developers who prefer working on the command line. For one, you can enable and disable rebalancing of the cluster, track and extract more details about cross-site replication relay nodes, and manage cache availability. Additionally, if you use the general &lt;code&gt;oc&lt;/code&gt; OpenShift command, you can now also &lt;a href="https://access.redhat.com/documentation/en-us/red_hat_data_grid/8.3/guide/d510c8ad-e097-4a3e-af55-e1d7967ecac3" rel="noopener noreferrer"&gt;install&lt;/a&gt; the Infinispan extension and use it with &lt;code&gt;oc&lt;/code&gt;, as shown in Figure 3.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdevelopers.redhat.com%2Fsites%2Fdefault%2Ffiles%2Fstyles%2Farticle_full_width_1440px_w%2Fpublic%2Fcontrol.png%3Fitok%3DheoMJq24" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdevelopers.redhat.com%2Fsites%2Fdefault%2Ffiles%2Fstyles%2Farticle_full_width_1440px_w%2Fpublic%2Fcontrol.png%3Fitok%3DheoMJq24" title="image_tooltip" alt="alt_text" width="427" height="258"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 3. You can control a Data Grid cluster from the command line.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Usability improvements&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;We've made a few more improvements to increase the usability of Data Grid in this release:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data Grid 8.3 has full support for &lt;a href="https://developers.redhat.com/articles/2021/12/14/explore-java-17-language-features-quarkus" rel="noopener noreferrer"&gt;Java 17&lt;/a&gt; for embedded and remote caches.&lt;/li&gt;
&lt;li&gt;You can automatically migrate any file-store configuration during the upgrade to Data Grid 8.3.&lt;/li&gt;
&lt;li&gt;You can now delete entries with the &lt;a href="https://access.redhat.com/documentation/ru-ru/red_hat_data_grid/7.1/html/developer_guide/building_ickle_query" rel="noopener noreferrer"&gt;Ickle programming language&lt;/a&gt;:&lt;/li&gt;
&lt;li&gt;&lt;code&gt;query.&lt;strong&gt;create&lt;/strong&gt;("&lt;strong&gt;DELETE&lt;/strong&gt; FROM books WHERE page_count &amp;gt; 500").executeStatement();&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Copy snippet&lt;/li&gt;
&lt;li&gt;Hot Rod migration is simpler when upgrading clusters of server nodes between versions and handles configuration changes transparently.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Get started with Data Grid 8.3&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Ready to dive in and try out Data Grid 8.3? These resources will get you started:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Zip distributions are available through the &lt;a href="https://access.redhat.com/jbossnetwork/restricted/listSoftware.html?product=data.grid&amp;amp;downloadType=distributions" rel="noopener noreferrer"&gt;Certified Service Provider (CSP) program&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Container distributions and Operators are available in the &lt;a href="https://access.redhat.com/containers/#/product/JbossDataGrid" rel="noopener noreferrer"&gt;Red Hat Container Catalog&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Product documentation is available on the &lt;a href="https://access.redhat.com/documentation/en-us/red_hat_data_grid/8.0/" rel="noopener noreferrer"&gt;Red Hat customer portal&lt;/a&gt;, including &lt;a href="https://access.redhat.com/documentation/en-us/red_hat_data_grid/8.0/html/data_grid_migration_guide/" rel="noopener noreferrer"&gt;a migration guide&lt;/a&gt; to help you migrate your existing Data Grid deployments to 8.0.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Visit the &lt;a href="https://developers.redhat.com/products/datagrid" rel="noopener noreferrer"&gt;Red Hat Data Grid&lt;/a&gt; product page to learn more about this technology.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Recent Articles&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://developers.redhat.com/articles/2022/02/24/inspecting-containerized-python-applications-cluster" rel="noopener noreferrer"&gt;Inspecting containerized Python applications in a cluster&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://developers.redhat.com/articles/2022/02/24/sql-cache-stores-and-more-data-grid-83" rel="noopener noreferrer"&gt;SQL cache stores and more in Data Grid 8.3&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://developers.redhat.com/articles/2022/02/22/debug-net-applications-running-local-containers-vs-code" rel="noopener noreferrer"&gt;Debug .NET applications running in local containers with VS Code&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://developers.redhat.com/articles/2022/02/17/quality-testing-linux-kernel" rel="noopener noreferrer"&gt;Quality testing the Linux kernel&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://developers.redhat.com/articles/2022/02/16/code-specialization-mir-lightweight-jit-compiler" rel="noopener noreferrer"&gt;Code specialization for the MIR lightweight JIT compiler&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Related Content&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://developers.redhat.com/blog/2018/06/26/data-grid-multi-cloud-real-time-game" rel="noopener noreferrer"&gt;Using Red Hat Data Grid to power a multi-cloud real-time game&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://developers.redhat.com/blog/2017/05/30/implementing-a-log-collector-using-red-hat-jboss-fuse-and-red-hat-jboss-data-grid" rel="noopener noreferrer"&gt;Implementing a Log Collector using Red Hat JBoss Fuse and Red Hat JBoss Data Grid&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://developers.redhat.com/blog/2020/06/19/develop-and-test-a-quarkus-client-on-red-hat-codeready-containers-with-red-hat-data-grid-8-0" rel="noopener noreferrer"&gt;Develop and test a Quarkus client on Red Hat CodeReady Containers with Red Hat Data Grid 8.0&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://developers.redhat.com/blog/2019/03/25/five-layers-of-security-for-red-hat-data-grid-on-openshift" rel="noopener noreferrer"&gt;Five layers of security for Red Hat Data Grid on OpenShift&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>java</category>
      <category>cache</category>
      <category>microservices</category>
      <category>infinispan</category>
    </item>
    <item>
      <title>What's new for developers in Java 18</title>
      <dc:creator>Shaaf Syed</dc:creator>
      <pubDate>Thu, 27 Jan 2022 10:33:46 +0000</pubDate>
      <link>https://forem.com/sshaaf/whats-new-for-developers-in-java-18-2m61</link>
      <guid>https://forem.com/sshaaf/whats-new-for-developers-in-java-18-2m61</guid>
      <description>&lt;p&gt;In exciting news for &lt;a href="https://developers.redhat.com/topics/enterprise-java" rel="noopener noreferrer"&gt;Java&lt;/a&gt; developers, Java 18 forked off from the main line &lt;a href="https://www.infoq.com/news/2021/12/java-news-roundup-dec06-2021/" rel="noopener noreferrer"&gt;at the end of last year&lt;/a&gt; and has entered &lt;a href="https://openjdk.java.net/projects/jdk/18/" rel="noopener noreferrer"&gt;Rampdown Phase Two&lt;/a&gt;. This article highlights some of the features that developers can look for in the upcoming Java 18 release, including the new simple web server module, a more sophisticated way to annotate your Javadocs, and the &lt;code&gt;–finalization=disabled&lt;/code&gt; option, which lets you test how a Java application will behave when finalization is removed in a future release. See the end of the article for where to download Java 18 in early access builds.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Java's new simple web server&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Java 18 will provide a minimally functional web server in the &lt;code&gt;jdk.httpserver&lt;/code&gt; module. It has an API for access, as well as a binary in the &lt;code&gt;bin&lt;/code&gt; directory in case you want to run the server from the command line.&lt;/p&gt;

&lt;p&gt;The command to run the web server can be as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ jwebserver -b 0.0.0.0 -p 8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note:** For more command-line options and details about the &lt;code&gt;jdk.httpserver&lt;/code&gt; module, see the &lt;a href="https://openjdk.java.net/jeps/408" rel="noopener noreferrer"&gt;JEP 408 documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you are wondering whether you can implement a full-blown production web server using the simple web server APIs, the answer is no. The web server is definitely not intended for that use. For one thing, it communicates over HTTP/1.1 and doesn't support PUT requests, so it doesn't support dynamic content. The web server is recommended for prototyping, testing, and debugging. An example code snippet with comments follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import java.net.InetSocketAddress;
import java.nio.file.Path;
import com.sun.net.httpserver.SimpleFileServer;
import static com.sun.net.httpserver.SimpleFileServer.OutputLevel;

public class App {
public static void main( String[] args ){
    // Create a simple file server, given the socket address, path and output level
    var server = SimpleFileServer.createFileServer(new InetSocketAddress(8000),             Path.of("/home/java"), OutputLevel.VERBOSE);

    // Starting the server
    server.start();

    // A friendly message to get started.
    System.out.println( "We are getting started.. Hello World!" );
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Code snippets in Java API documentation&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Prior to Java 18, developers mostly inserted code samples in Javadoc comments using the &lt;code&gt;@code&lt;/code&gt; annotation. That technique is somewhat limited and requires workarounds. For instance, it has always been difficult to validate what's inside the code fragment, implement syntax highlighting, and insert links or escape characters.&lt;/p&gt;

&lt;p&gt;As a new approach, JEP 413 proposes using the &lt;code&gt;@snippet&lt;/code&gt; tag. As noted in the JEP, the tag "can be used to declare both &lt;em&gt;inline snippets&lt;/em&gt;, where the code fragment is included within the tag itself, and &lt;em&gt;external snippets&lt;/em&gt;, where the code fragment is read from a separate source file."&lt;/p&gt;

&lt;p&gt;Here is an example of an inline snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
* The following code shows how to use {@code Optional.isPresent}:
* {@snippet :
* if (v.isPresent()) {
*     System.out.println("v: " + v.get());
* }
* }
*/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code lies between curly braces, where the opening brace is followed by the &lt;code&gt;@snippet&lt;/code&gt; tag. The Javadoc utility now also includes options to specify links, highlight code, and more. See &lt;a href="https://openjdk.java.net/jeps/413" rel="noopener noreferrer"&gt;JEP 413&lt;/a&gt; for more details.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;UTF-8 character set by default&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In prior releases, the default character set was determined when the Java runtime started, and depended on the user's locale and default encoding. Thus, on Windows the charset was &lt;code&gt;windows-1252&lt;/code&gt;, whereas on macOS it was UTF-8 except in the POSIX C locale. With Java 18, UTF-8 will be the default for all operating systems.&lt;/p&gt;

&lt;p&gt;You can change the default charset by setting the &lt;code&gt;file.encoding&lt;/code&gt; to &lt;code&gt;COMPAT&lt;/code&gt;; for instance, by running &lt;code&gt;java -Dfile.encoding=COMPAT&lt;/code&gt;. This setting reverts to the algorithm in Java 17.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Prepare now for the removal of finalize()&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If you have developed Java applications, you are probably familiar with the &lt;code&gt;finalize()&lt;/code&gt; method. Since Java 9, the recommendation has been to not use &lt;code&gt;finalize()&lt;/code&gt;, but instead to use a &lt;em&gt;try-with-resources&lt;/em&gt; statement or the new Cleaner APIs. JEP 421 helps developers prepare for the eventual removal of finalization. You can run an application with the &lt;code&gt;–finalization=disabled&lt;/code&gt; option to see how it will behave without the &lt;code&gt;finalize()&lt;/code&gt; method. It's great to see this evolution. As a developer, it is also an opportunity to take a closer look at your application's behavior.&lt;/p&gt;

&lt;p&gt;**Note: **A recent &lt;a href="https://inside.java/2022/01/12/podcast-021/" rel="noopener noreferrer"&gt;Inside Java podcast episode&lt;/a&gt; discusses issues with finalization and what to expect in Java 18. See the documentation for &lt;a href="https://openjdk.java.net/jeps/421" rel="noopener noreferrer"&gt;JEP 421&lt;/a&gt; for more details.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Preview features in Java 18&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Some of the features that were included in the development phase of Java 18 are still in preview. These include the Vector API (&lt;a href="https://openjdk.java.net/jeps/417" rel="noopener noreferrer"&gt;JEP 417&lt;/a&gt;), which Gunnar Morling discusses in this &lt;a href="https://developers.redhat.com/devnation/tech-talks/java17-apis" rel="noopener noreferrer"&gt;DevNation Tech Talk&lt;/a&gt;. The foreign function and memory API (&lt;a href="https://openjdk.java.net/jeps/419" rel="noopener noreferrer"&gt;JEP 419&lt;/a&gt;) and pattern matching for &lt;code&gt;switch&lt;/code&gt; (&lt;a href="https://openjdk.java.net/jeps/420" rel="noopener noreferrer"&gt;JEP 420&lt;/a&gt;) also remain in preview.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Where to download Java 18&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To give Java 18 a try, you can download the early access &lt;a href="https://adoptium.net/archive.html?variant=openjdk18&amp;amp;jvmVariant=hotspot" rel="noopener noreferrer"&gt;Eclipse Temurin builds&lt;/a&gt; from Eclipse Adoptium. Temurin is the &lt;a href="https://adoptium.net/faq.html#temurinName" rel="noopener noreferrer"&gt;name of the OpenJDK distribution&lt;/a&gt; from Adoptium.&lt;/p&gt;

&lt;p&gt;If you are using a current version of Java and want to compare the features with Java 18, I would also highly recommend the &lt;a href="https://javaalmanac.io/" rel="noopener noreferrer"&gt;Java Almanac&lt;/a&gt;. It contains a &lt;a href="https://javaalmanac.io/jdk/18/" rel="noopener noreferrer"&gt;page listing all the new features and changes in Java 18&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This article was a quick look at some of the highlights of Java 18. Keep an eye on the &lt;a href="https://developers.redhat.com/products/openjdk" rel="noopener noreferrer"&gt;Red Hat Developer OpenJDK page&lt;/a&gt; for more Java 18 updates. You can also get started using the &lt;a href="https://developers.redhat.com/products/openjdk/getting-started" rel="noopener noreferrer"&gt;Red Hat build of OpenJDK&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>java</category>
      <category>programming</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
