<?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: anand jaisy</title>
    <description>The latest articles on Forem by anand jaisy (@anand_jaisy_2f7644a12001b).</description>
    <link>https://forem.com/anand_jaisy_2f7644a12001b</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%2F1711482%2F5d4ff2e4-dc32-4eb4-9fb0-ec03173f3dc4.jpg</url>
      <title>Forem: anand jaisy</title>
      <link>https://forem.com/anand_jaisy_2f7644a12001b</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/anand_jaisy_2f7644a12001b"/>
    <language>en</language>
    <item>
      <title>Java Is Modern—So Why Does It Still Feel Verbose?</title>
      <dc:creator>anand jaisy</dc:creator>
      <pubDate>Mon, 26 Jan 2026 00:40:33 +0000</pubDate>
      <link>https://forem.com/anand_jaisy_2f7644a12001b/java-is-modern-so-why-does-it-still-feel-verbose-460a</link>
      <guid>https://forem.com/anand_jaisy_2f7644a12001b/java-is-modern-so-why-does-it-still-feel-verbose-460a</guid>
      <description>&lt;p&gt;Java’s six-month release cycle has significantly accelerated its evolution, transforming it into a more modern and capable platform. Java has long been recognized as the backbone of enterprise applications, valued for its stability, performance, and widespread adoption.&lt;/p&gt;

&lt;p&gt;With recent advancements, Java has unquestionably modernized. However, developers still perceive certain aspects—particularly verbosity and boilerplate code—as excessive when compared to other programming languages.&lt;/p&gt;

&lt;p&gt;A common example is entity modeling using Spring Data or Micronaut Data with JPA:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Entity
public class Role extends EntityMetadata {
    private String name;
    private String description;

    // Create getter/setter constructor and other things
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In such cases, developers are required to manually write getters, setters, constructors, and other boilerplate code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lombok as a Solution
&lt;/h2&gt;

&lt;p&gt;Lombok addresses this issue effectively:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity
public class Role extends EntityMetadata {
    private String name;
    private String description;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By leveraging Lombok, developers can significantly reduce boilerplate, improving readability and productivity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trade-off:&lt;/strong&gt;&lt;br&gt;
The primary drawback is the reliance on a third-party library, which some teams prefer to avoid for long-term maintainability or tooling reasons.&lt;/p&gt;
&lt;h2&gt;
  
  
  Records and Their Limitations
&lt;/h2&gt;

&lt;p&gt;In many Java conference talks and community discussions, architects often recommend using Java records. While records are a powerful addition, they come with limitations—especially in enterprise contexts.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Entity
public record Role(String name, String description) extends EntityMetadata {
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Records cannot extend other classes (they implicitly extend java.lang.Record), making them unsuitable where inheritance is a core part of object-oriented design. Additionally, records do not integrate well with JPA, which expects mutable entities and a no-argument constructor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quarkus Approach
&lt;/h2&gt;

&lt;p&gt;The Quarkus team has introduced an interesting and developer-friendly alternative:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Entity
public class Role extends EntityMetadata {
    public String name;
    public String description;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By allowing public fields, Quarkus generates the necessary accessors and other infrastructure behind the scenes. This approach reduces boilerplate without requiring additional libraries, offering a compelling balance between simplicity and productivity.&lt;/p&gt;

&lt;p&gt;We all know there are lots of project going on with JAVA platform, however these small things make huge difference in developer adoption. That why JAVA is still famous for too much verbosity and boilaplate code.&lt;/p&gt;

&lt;p&gt;The Java ecosystem continues to grow, with numerous frameworks and platforms actively evolving. However, seemingly small concerns—such as verbosity and boilerplate—have a significant impact on developer experience and adoption.&lt;/p&gt;

</description>
      <category>java</category>
      <category>spring</category>
      <category>micronaut</category>
      <category>quarkus</category>
    </item>
    <item>
      <title>Micronaut vs Quarkus: Why I Switched After Two Years</title>
      <dc:creator>anand jaisy</dc:creator>
      <pubDate>Thu, 22 Jan 2026 14:13:02 +0000</pubDate>
      <link>https://forem.com/anand_jaisy_2f7644a12001b/micronaut-vs-quarkus-why-i-switched-after-two-years-5e94</link>
      <guid>https://forem.com/anand_jaisy_2f7644a12001b/micronaut-vs-quarkus-why-i-switched-after-two-years-5e94</guid>
      <description>&lt;p&gt;&lt;a href="https://micronaut.io/" rel="noopener noreferrer"&gt;Micronaut&lt;/a&gt; is a modern, JVM-based, full-stack framework designed for building modular, highly testable microservices and serverless applications. After working with Micronaut for over two years, I decided to transition to &lt;a href="https://quarkus.io/" rel="noopener noreferrer"&gt;Quarkus&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Both frameworks are well regarded in the microservices ecosystem, but after hands-on experience with each, I found Quarkus to offer several practical advantages that better align with my development workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Ground
&lt;/h2&gt;

&lt;p&gt;Both Micronaut and Quarkus provide excellent CLI support and can be easily installed using SDKMAN:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sdk install quarkus
sdk install micronaut
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both frameworks emphasize fast startup times, low memory usage, and cloud-native microservice design.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Switch to Quarkus?
&lt;/h2&gt;

&lt;h2&gt;
  
  
  1. Developer Mode (Dev Mode)
&lt;/h2&gt;

&lt;p&gt;Quarkus Dev Mode is one of its most powerful features. You can start the application in development mode using a single command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk2h66ftug92c6p3ndyq5.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%2Fk2h66ftug92c6p3ndyq5.png" alt=" " width="800" height="166"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this mode, any change made anywhere in the codebase is automatically detected and hot-reloaded, providing near-instant feedback.&lt;/p&gt;

&lt;p&gt;Micronaut offers a similar capability using Gradle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./gradlew run --continuous
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./gradlew run --t
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Testcontainers Auto-Configuration
&lt;/h2&gt;

&lt;p&gt;Quarkus provides seamless Testcontainers integration out of the box. When a project is created, test resources are automatically configured—no additional setup is required.&lt;/p&gt;

&lt;p&gt;In Micronaut, Testcontainers support must be explicitly enabled by adding a plugin dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;id("io.micronaut.test-resources") version "4.6.1"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This additional configuration introduces extra overhead, especially during initial project setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. No Lombok Required for Entities
&lt;/h2&gt;

&lt;p&gt;Quarkus significantly reduces boilerplate code by allowing public fields in entities and providing powerful ORM tooling via Panache.&lt;/p&gt;

&lt;p&gt;For example, with Quarkus:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Entity
public class Billing extends Audit{
    public UUID jobId;
    public UUID subscriptionId;
    public Long amount;
    @Enumerated(EnumType.STRING)
    public Status status;
    public String receiptUrl;
    public Instant paidDate;
    public String paymentMethod;
    public String paymentProvider;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is further enhanced by the Panache REST Data extension:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;implementation("io.quarkus:quarkus-hibernate-orm-rest-data-panache")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Micronaut, Lombok is typically required to avoid excessive boilerplate. Without Lombok, developers must manually write getters and setters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Introspected
@AllArgsConstructor
@Setter
@Getter
@Entity
public class Billing extends Audit{
    private UUID jobId;
    private UUID subscriptionId;
    private Long amount;
    @Enumerated(EnumType.STRING)
    private Status status;
    private String receiptUrl;
    private Instant paidDate;
    private String paymentMethod;
    private String paymentProvider;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While Lombok works well, it adds an additional dependency and build-time complexity.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Test-Driven Development (TDD) Experience
&lt;/h2&gt;

&lt;p&gt;Quarkus offers an excellent TDD workflow. During test execution, developers can press R to re-run tests continuously. This allows instant feedback when code changes break existing functionality, enabling a true “test-first” development cycle.&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%2F6adzs0quktq6bqa2blk6.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%2F6adzs0quktq6bqa2blk6.png" alt=" " width="800" height="45"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Micronaut currently does not provide an equivalent built-in feature for continuous test reruns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Documentation
&lt;/h2&gt;

&lt;p&gt;In this area, Micronaut has an edge. Its documentation is more comprehensive, structured, and beginner-friendly compared to Quarkus.&lt;/p&gt;

&lt;h2&gt;
  
  
  Community
&lt;/h2&gt;

&lt;p&gt;Although Quarkus is newer than Micronaut, its community has grown rapidly. Backed by Red Hat and widely adopted in the enterprise Java ecosystem, Quarkus benefits from strong community engagement, frequent updates, and extensive third-party integrations.&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%2F98z46tdetmkj0tytoisu.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%2F98z46tdetmkj0tytoisu.png" alt=" " width="800" height="93"&gt;&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjtwjf5x841525nn2zd95.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%2Fjtwjf5x841525nn2zd95.png" alt=" " width="780" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Micronaut
&lt;/h2&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%2Fbuqcjqc87hec49koz70i.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%2Fbuqcjqc87hec49koz70i.png" alt=" " width="800" height="98"&gt;&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl6fy6y3ms3v6hapdvg6t.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%2Fl6fy6y3ms3v6hapdvg6t.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Both Micronaut and Quarkus are excellent frameworks for building microservices. However, Quarkus stands out in areas such as developer productivity, dev mode experience, test automation, and reduced boilerplate.&lt;/p&gt;

&lt;p&gt;Micronaut remains a strong choice—particularly due to its documentation—but for my workflow and long-term maintainability goals, Quarkus has proven to be the better fit.&lt;/p&gt;

</description>
      <category>java</category>
      <category>micronaut</category>
      <category>quarkus</category>
    </item>
    <item>
      <title>Data Oriented Rest Api in Java</title>
      <dc:creator>anand jaisy</dc:creator>
      <pubDate>Mon, 19 Jan 2026 07:37:35 +0000</pubDate>
      <link>https://forem.com/anand_jaisy_2f7644a12001b/data-oriented-rest-api-in-java-geg</link>
      <guid>https://forem.com/anand_jaisy_2f7644a12001b/data-oriented-rest-api-in-java-geg</guid>
      <description>&lt;h2&gt;
  
  
  Data-oriented
&lt;/h2&gt;

&lt;p&gt;Data-oriented programming promotes modeling information as immutable data and separating the business logic that operates on it. As the shift toward simpler, more focused programs has gained momentum, Java has introduced new features to better support this style—such as records for straightforward data modeling, sealed classes for representing fixed sets of alternatives, and pattern matching for flexible and expressive handling of polymorphic data.&lt;/p&gt;

&lt;p&gt;Records, sealed classes, and pattern matching are complementary features in Java that collectively enhance support for data-oriented programming.&lt;/p&gt;

&lt;p&gt;Records provide a concise way to model immutable data structures.&lt;br&gt;
Sealed classes enable precise modeling of constrained hierarchies, allowing developers to define a fixed set of subclasses.&lt;br&gt;
Pattern matching offers a type-safe and expressive mechanism for working with polymorphic data.&lt;br&gt;
Java has introduced pattern matching in progressive stages:&lt;/p&gt;

&lt;p&gt;Initially, only type-test patterns were supported in instanceof checks.&lt;br&gt;
Subsequent enhancements extended support to switch statements, enabling more expressive control flow.&lt;br&gt;
Most recently, deconstruction patterns for records were introduced in Java 19, allowing developers to extract and operate on the internal components of records in a streamlined, declarative manner.&lt;br&gt;
These features together promote cleaner, more maintainable code by aligning data representation closely with the logic that operates on it.&lt;/p&gt;

&lt;p&gt;Reference - &lt;a href="https://www.infoq.com/articles/data-oriented-programming-java/" rel="noopener noreferrer"&gt;https://www.infoq.com/articles/data-oriented-programming-java/&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Building a REST Endpoint Using Java Sealed Interfaces and Records
&lt;/h2&gt;

&lt;p&gt;In this article, we’ll build a REST endpoint using Java sealed interfaces and records to model success and failure outcomes in a clean, expressive, and type-safe way.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The same approach applies across popular Java frameworks such as Micronaut, Spring Boot, and Quarkus. For demonstration purposes, I’ll use Micronaut, but you can easily adapt the examples to the framework you’re most comfortable with.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Modeling Input and Output with Sealed Interfaces
&lt;/h2&gt;

&lt;p&gt;We start by defining a common result type that represents either a successful outcome or a failure. Java’s sealed interfaces allow us to strictly control which implementations are permitted, while records provide concise, immutable data carriers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public sealed interface IResult
        permits IResult.Success, IResult.Failure {

    record Success&amp;lt;T&amp;gt;(T value) implements IResult {}
    record Failure&amp;lt;T&amp;gt;(Exception error) implements IResult {}

    static &amp;lt;T&amp;gt; IResult success(T value) {
        return new Success&amp;lt;&amp;gt;(value);
    }

    static IResult failure(Exception ex) {
        return new Failure&amp;lt;&amp;gt;(ex);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Success wraps a successful result&lt;/li&gt;
&lt;li&gt;Failure wraps an exception&lt;/li&gt;
&lt;li&gt;Static factory methods improve readability and remove the need for the new keyword at call sites
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;return IResult.failure(new ItemNotFoundException(ApplicationMessage.ValidationFieldMessage.RECORD_NOT_FOUND));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Alternative: Without Static Factory Methods&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public sealed interface IResult
        permits IResult.Success, IResult.Failure {

    record Success&amp;lt;T&amp;gt;(T value) implements IResult {}
    record Failure&amp;lt;T&amp;gt;(Exception error) implements IResult {}
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;return new IResult.failure(new ItemNotFoundException(ApplicationMessage.ValidationFieldMessage.RECORD_NOT_FOUND));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only difference is the explicit use of the new keyword. Functionally, both approaches are equivalent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Controller Implementation
&lt;/h2&gt;

&lt;p&gt;Next, let’s look at how this result abstraction simplifies controller logic. By leveraging pattern matching with switch, we can map domain results directly to HTTP responses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Controller("/privacyPreference")
public class PrivacyPreferenceController implements IHttpAction&amp;lt;PrivacyPreferenceRecord.PrivacyPreferenceRequest, PrivacyPreferenceRequest, PrivacyPreferenceResponse&amp;gt; {
    private final IPrivacyPreferenceRepo&amp;lt;PrivacyPreferenceResponse, PrivacyPreferenceRequest, FilterSortRequest&amp;gt; iPrivacyPreferenceRepo;

    public PrivacyPreferenceController(IPrivacyPreferenceRepo&amp;lt;PrivacyPreferenceResponse, PrivacyPreferenceRequest, FilterSortRequest&amp;gt; iPrivacyPreferenceRepo) {
        this.iPrivacyPreferenceRepo = iPrivacyPreferenceRepo;
    }

    @Override
    public HttpResponse&amp;lt;?&amp;gt; find() {
        var result = this.iPrivacyPreferenceRepo.find();
        return switch (result) {
            case Success success -&amp;gt; HttpResponse.ok(success.value());
            case Failure failure -&amp;gt; HttpResponse.notFound(failure.error().getMessage());
        };
    }

    @Override
    public HttpResponse&amp;lt;?&amp;gt; get(@Nonnull UUID id) {
        var result = this.iPrivacyPreferenceRepo.get(id);
        return switch (result) {
            case Success success -&amp;gt; HttpResponse.ok(success.value());
            case Failure failure -&amp;gt; HttpResponse.notFound(failure.error().getMessage());
        };
    }

    @Override
    public HttpResponse&amp;lt;?&amp;gt; post(PrivacyPreferenceRecord.@Valid PrivacyPreferenceRequest request) {
        var result = this.iPrivacyPreferenceRepo.create(request);
        return switch (result) {
            case Success success -&amp;gt; HttpResponse.ok(success.value());
            case Failure failure -&amp;gt; HttpResponse.badRequest(failure.error().getMessage());
        };
    }

    @Override
    public HttpResponse&amp;lt;?&amp;gt; patch(@NotNull UUID id, PrivacyPreferenceRecord.PrivacyPreferenceRequest request) {
        var result = this.iPrivacyPreferenceRepo.update(id, request);
        return switch (result) {
            case Success success -&amp;gt; HttpResponse.ok(success.value());
            case Failure failure -&amp;gt; switch (failure.error()) {
                case DuplicateNameException e -&amp;gt; HttpResponse.badRequest(e.getMessage());
                case ItemNotFoundException e -&amp;gt; HttpResponse.notFound(e.getMessage());
                default -&amp;gt; HttpResponse.serverError(failure.error().getMessage());
            };
        };
    }

    @Override
    public HttpResponse&amp;lt;?&amp;gt; delete(@NotNull UUID id) {
        var result = this.iPrivacyPreferenceRepo.delete(id);
        return switch (result) {
            case Success _ -&amp;gt; HttpResponse.noContent();
            case Failure failure -&amp;gt; HttpResponse.badRequest(failure.error().getMessage());
        };
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why This Works Well&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No exception-driven control flow in the controller&lt;/li&gt;
&lt;li&gt;Compile-time safety via sealed types&lt;/li&gt;
&lt;li&gt;Clear, readable HTTP mapping using pattern matching&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Repository Contract
&lt;/h2&gt;

&lt;p&gt;The repository interface consistently returns IResult, ensuring that all consumers handle success and failure explicitly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public interface IPrivacyPreferenceRepo&amp;lt;R, T, C&amp;gt; {
    IResult find();
    IResult get(@NotNull UUID var1);
    IResult create(T var1);
    IResult update(@NotNull UUID var1, T var2);
    IResult delete(@NotNull UUID var1);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Singleton
public class PrivacyPreferenceRepository implements IPrivacyPreferenceRepo&amp;lt;PrivacyPreferenceResponse, PrivacyPreferenceRequest, FilterSortRequest&amp;gt; {
    private final Map&amp;lt;UUID, PrivacyPreference&amp;gt; privacyPreferenceMap;
    private final PrivacyPreferenceEntityMapper privacyPreferenceEntityMapper;
    private final PrivacyPreferenceResponseMapper privacyPreferenceResponseMapper;

    public PrivacyPreferenceRepository(RootProvider&amp;lt;Root&amp;gt; rootProvider, PrivacyPreferenceEntityMapper privacyPreferenceEntityMapper,
                                       PrivacyPreferenceResponseMapper privacyPreferenceResponseMapper) {
        this.privacyPreferenceMap = rootProvider.root().getPrivacyPreference();
        this.privacyPreferenceEntityMapper = privacyPreferenceEntityMapper;
        this.privacyPreferenceResponseMapper = privacyPreferenceResponseMapper;
    }

    @Override
    public IResult find() {
        return IResult.success(privacyPreferenceMap.values().stream()
                .map(privacyPreferenceResponseMapper)
                .toList());
    }

    @Override
    public IResult get(@NotNull UUID id) {
        PrivacyPreference privacyPreference = privacyPreferenceMap.get(id);
        if (privacyPreference == null)
            return IResult.failure(new ItemNotFoundException(ApplicationMessage.ValidationFieldMessage.RECORD_NOT_FOUND));

        return IResult.success(this.privacyPreferenceResponseMapper.apply(privacyPreference));
    }

    @Override
    public IResult create(PrivacyPreferenceRequest request) {
        if (privacyPreferenceMap.values().stream().anyMatch(x -&amp;gt; x.userId().equals(request.userId())))
            return IResult.failure(new DuplicateNameException(ApplicationMessage.ApplicationError.RECORD_ALREADY_EXISTS));

        var privacyPreference = this.privacyPreferenceEntityMapper.apply(request, Optional.empty());
        this.save(this.privacyPreferenceMap, privacyPreference);
        return IResult.success(privacyPreferenceResponseMapper.apply(privacyPreference));
    }

    @Override
    public IResult update(@NotNull UUID id, PrivacyPreferenceRequest request) {
        PrivacyPreference privacyPreference = privacyPreferenceMap.get(id);
        if (privacyPreference == null)
            return IResult.failure(new ItemNotFoundException(ApplicationMessage.ValidationFieldMessage.RECORD_NOT_FOUND));

        var updatePrivacyPreference =  this.privacyPreferenceEntityMapper.apply(request, Optional.of(privacyPreference));
        save(this.privacyPreferenceMap, updatePrivacyPreference);
        return IResult.success(privacyPreferenceResponseMapper.apply(updatePrivacyPreference));
    }

    @Override
    public IResult delete(@NotNull UUID id) {
        try {
            deleteCourseById(this.privacyPreferenceMap, id);
        } catch (Exception e) {
            return IResult.failure(new ItemNotFoundException(ApplicationMessage.AdditionalRequirementMessage.ADDITIONAL_REQUIREMENT_NOTFOUND));
        }
        return IResult.success(true);
    }

    @StoreParams("additionalRequirement")
    protected void save(Map&amp;lt;UUID, PrivacyPreference&amp;gt; additionalRequirement, @NonNull PrivacyPreference request) {
        additionalRequirement.put(request.id(), request);
    }

    @StoreParams("additionalRequirement")
    protected void deleteCourseById(Map&amp;lt;UUID, PrivacyPreference&amp;gt; additionalRequirement,@NonNull UUID id) {
        if (additionalRequirement.get(id) == null)
            throw new ItemNotFoundException(ApplicationMessage.AdditionalRequirementMessage.ADDITIONAL_REQUIREMENT_NOTFOUND);
        additionalRequirement.remove(id);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Alternative Functional style
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Serdeable
public class Result&amp;lt;T&amp;gt; {
    public final Exception exception;
    public final T value;
    private final ResultState state;

    public Result(T value) {
        this.exception = null;
        this.value = value;
        state = ResultState.SUCCESS;
    }

    public Result(Exception e) {
        this.exception = e;
        this.value = null;
        state = ResultState.FAULTED;
    }

    public static &amp;lt;T&amp;gt; Result&amp;lt;T&amp;gt; of(T value) {
        return new Result&amp;lt;&amp;gt;(value);
    }

    public static &amp;lt;T&amp;gt; Result&amp;lt;T&amp;gt; of(Exception e) {
        return new Result&amp;lt;&amp;gt;(e);
    }

    public boolean isFaulted() {
        return state == ResultState.FAULTED;
    }

    public boolean isSuccess() {
        return state == ResultState.SUCCESS;
    }
    public &amp;lt;R&amp;gt; R match(Function&amp;lt;T, R&amp;gt; successFn, Function&amp;lt;Exception, R&amp;gt; fail) {
        try {
            if (state == ResultState.FAULTED)
                return fail.apply(this.exception);
            else
                return successFn.apply(this.value);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public &amp;lt;R&amp;gt; R match(Supplier&amp;lt;R&amp;gt; successFn, Supplier&amp;lt;R&amp;gt; fail) {
        try {
            if (state == ResultState.FAULTED)
                return fail.get();
            else
                return successFn.get();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public &amp;lt;R&amp;gt; R match(Function&amp;lt;T, R&amp;gt; successFn, Supplier&amp;lt;R&amp;gt; fail) {
        try {
            if (state == ResultState.FAULTED)
                return fail.get();
            else
                return successFn.apply(this.value);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public &amp;lt;R&amp;gt; R match(Supplier&amp;lt;R&amp;gt; successFn, Function&amp;lt;Exception, R&amp;gt;  fail) {
        try {
            if (state == ResultState.FAULTED)
                return fail.apply(this.exception);
            else
                return successFn.get();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Usage&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Repository&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 Result&amp;lt;AdditionalRequirementResponse&amp;gt; create(AdditionalRequirementRequest tagRequest) {
        if (additionalRequirementData.values().stream().anyMatch(x -&amp;gt; x.name().equals(tagRequest.name())))
            return Result.of(new DuplicateNameException(ApplicationMessage.AdditionalRequirementMessage.ADDITIONAL_REQUIREMENT_NAME_DUPLICATE));

        var tag = additionalRequirementRequestMapper.apply(Optional.empty(),tagRequest);
        this.save(this.additionalRequirementData, tag);
        return Result.of(additionalRequirementResponseMapper.apply(tag));
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Controller&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    public HttpResponse&amp;lt;?&amp;gt; post(AdditionalRequirementRequest perkRequest) {
        var result = this.iEclipseStoreRepository.create(perkRequest);
        return result.match(success-&amp;gt; HttpResponse.ok(result.value), ()-&amp;gt;HttpResponse.badRequest(result.exception.getMessage()));
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Using sealed interfaces + records for API responses provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Strong domain modeling&lt;/li&gt;
&lt;li&gt;Explicit success/failure handling&lt;/li&gt;
&lt;li&gt;Cleaner controllers&lt;/li&gt;
&lt;li&gt;Compile-time exhaustiveness checking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This pattern scales well and works consistently across Micronaut, Spring Boot, and Quarkus, making it a solid choice for modern Java REST APIs.&lt;/p&gt;

</description>
      <category>java</category>
      <category>micronaut</category>
      <category>restapi</category>
    </item>
    <item>
      <title>The JVM’s Greatest Irony: Brilliant Engineering, Painful Scripting</title>
      <dc:creator>anand jaisy</dc:creator>
      <pubDate>Sat, 08 Nov 2025 03:05:53 +0000</pubDate>
      <link>https://forem.com/anand_jaisy_2f7644a12001b/scripting-in-java-nightmare-2pnj</link>
      <guid>https://forem.com/anand_jaisy_2f7644a12001b/scripting-in-java-nightmare-2pnj</guid>
      <description>&lt;p&gt;The Java Virtual Machine (JVM) is one of the most remarkable pieces of engineering in the software world. Over the last three decades, it has evolved through countless optimizations, design refinements, and community contributions. With the recent release of Java 25, the platform continues to demonstrate its commitment to performance, stability, and innovation — not just in runtime efficiency but also in language design and developer experience.&lt;/p&gt;

&lt;p&gt;That said, sometimes it’s the small things that matter most to developers. While Java is an industrial-grade powerhouse, scripting in Java can still feel like a nightmare compared to lightweight languages such as Python.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧩 Example: Generating RSA Private and Public Keys
&lt;/h2&gt;

&lt;p&gt;Here’s a simple Python script that generates a private RSA key and a public JWKS (JSON Web Key Set):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from jwcrypto import jwk
import json, uuid

key = jwk.JWK.generate(kty='RSA', size=2048)
key_id = str(uuid.uuid4())
key['kid'] = key_id
key['use'] = 'sig'
key['alg'] = 'RS256'

print("Private Key (keep secret):")
print(key.export_private())
print("\nPublic JWKS:")
print(json.dumps({"keys": [json.loads(key.export_public())]}, indent=2))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running it is 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;python generate_jwks.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clean, readable, and done.&lt;/p&gt;

&lt;h2&gt;
  
  
  ☕ Doing the Same in Java
&lt;/h2&gt;

&lt;p&gt;Let’s attempt the same thing in Java using the Nimbus JOSE + JWT library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import com.nimbusds.jose.jwk.*;
import com.nimbusds.jose.jwk.gen.*;
import net.minidev.json.JSONObject;
import java.util.UUID;

    void main() {
        RSAKey rsaJWK = new RSAKeyGenerator(2048)
                .keyID(UUID.randomUUID().toString())
                .algorithm(com.nimbusds.jose.JWSAlgorithm.RS256)
                .keyUse(KeyUse.SIGNATURE)
                .generate();


        IO.println("Private Key (keep secret):");
        IO.println(rsaJWK.toJSONString());


        RSAKey rsaPublicJWK = rsaJWK.toPublicJWK();
        JSONObject publicJWKS = new JSONObject();
        publicJWKS.put("keys", new Object[]{rsaPublicJWK.toJSONObject()});

        IO.println("\nPublic JWKS:");
        IO.println(publicJWKS.toJSONString());
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first glance, the Java code looks fine — structured, type-safe, and easy to follow.&lt;br&gt;
But the real challenge comes when you try to run it.&lt;/p&gt;
&lt;h2&gt;
  
  
  ⚙️ The Developer Reality
&lt;/h2&gt;

&lt;p&gt;Now the main pain comes when we try to run this piece of code. When we try to compile this we will get bunch of error due to dependency errors such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import com.nimbusds.jose.jwk.*;
import com.nimbusds.jose.jwk.gen.*;
import net.minidev.json.JSONObject;
import java.util.UUID
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To fix this, you’ll need to include external dependencies. For example, using Maven, you would add:&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;dependency&amp;gt;
  &amp;lt;groupId&amp;gt;com.nimbusds&amp;lt;/groupId&amp;gt;
  &amp;lt;artifactId&amp;gt;nimbus-jose-jwt&amp;lt;/artifactId&amp;gt;
  &amp;lt;version&amp;gt;9.40&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then compile and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mvn compile
mvn exec:java -Dexec.mainClass="com.example.App"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And just like that, what was a one-liner execution in Python becomes a mini build process in Java.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 The Modern Way — Run Java Like a Script Using &lt;a href="https://www.jbang.dev/" rel="noopener noreferrer"&gt;JBang&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.jbang.dev/" rel="noopener noreferrer"&gt;JBang&lt;/a&gt; a brilliant tool that lets you run Java files just like Python scripts, with zero project setup or Maven configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install using &lt;a href="https://sdkman.io/" rel="noopener noreferrer"&gt;sdkman&lt;/a&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sdk install jbang
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;///usr/bin/env jbang "$0" "$@" ; exit $?


//DEPS com.nimbusds:nimbus-jose-jwt:9.40
//DEPS org.json:json:20250517


import static com.nimbusds.jose.JWSAlgorithm.RS256;
import static com.nimbusds.jose.jwk.KeyUse.SIGNATURE;
import static java.lang.IO.println;
import static java.util.UUID.randomUUID;

import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.jwk.gen.RSAKeyGenerator;
import org.json.JSONObject;

void main() throws JOSEException {
        var key = new RSAKeyGenerator(2048)
                .keyID(randomUUID().toString())
                .keyUse(SIGNATURE)
                .algorithm(RS256)
                .generate();

        println("Private Key (keep secret):");
        println(key.toJSONString());

        println("\nPublic JWKS:");
        println(new JSONObject(Map.of("keys", new Object[]{key.toPublicJWK().toJSONObject()})).toString(2));

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

&lt;/div&gt;



&lt;p&gt;Save this file as &lt;code&gt;GenerateJWK.java&lt;/code&gt; and run it directly — no &lt;code&gt;pom.xml&lt;/code&gt;, no setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jbang GenerateJWK.java
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;JBang will automatically:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download the required dependencies (nimbus-jose-jwt:9.40)&lt;/li&gt;
&lt;li&gt;Compile your code&lt;/li&gt;
&lt;li&gt;Run it in a single step&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Just like Python or Node.js.&lt;/p&gt;

</description>
      <category>java</category>
      <category>webdev</category>
      <category>jbang</category>
      <category>jshell</category>
    </item>
    <item>
      <title>Pattern Matching in Java: The Modern Way to Write Cleaner Code</title>
      <dc:creator>anand jaisy</dc:creator>
      <pubDate>Thu, 31 Jul 2025 22:03:05 +0000</pubDate>
      <link>https://forem.com/anand_jaisy_2f7644a12001b/pattern-matching-in-java-the-modern-way-to-write-cleaner-code-8gc</link>
      <guid>https://forem.com/anand_jaisy_2f7644a12001b/pattern-matching-in-java-the-modern-way-to-write-cleaner-code-8gc</guid>
      <description>&lt;p&gt;Pattern matching streamlines your code by allowing you to check whether a value matches a specific structure or type—eliminating the need for repetitive if statements and manual type casting. It enables cleaner, more readable code by letting Java handle the complex checks for you.&lt;/p&gt;

&lt;p&gt;Pattern matching in Java helps developers write more concise and efficient code. For instance, instead of manually checking whether an object is a String and then casting it, pattern matching allows you to perform both operations in a single, streamlined step.&lt;/p&gt;

&lt;p&gt;Key Benefits of Pattern Matching:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reduced Boilerplate: No need for repetitive instanceof checks followed by explicit casting. The code becomes shorter and more elegant.&lt;/li&gt;
&lt;li&gt;Improved Readability: Type checks and variable declarations happen together, making the logic easier to understand at a glance.&lt;/li&gt;
&lt;li&gt;Fewer Errors: Since Java handles the casting automatically, it reduces the risk of runtime exceptions like ClassCastException.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Naive Approach to Type Checking in Java
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (object instanceof String) {
    String s = (String) object;
    System.out.println(s.length());
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Better approach
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (object instanceof String s) {
    System.out.println(s.length());
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The benefits are clear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No redundant casting: The variable s is immediately available as a String, with no need for manual type conversion.&lt;/li&gt;
&lt;li&gt;Cleaner logic: Type checking and variable assignment are combined in a single, concise line.&lt;/li&gt;
&lt;li&gt;Fewer bugs: Eliminates the risk of ClassCastException by letting Java safely handle the casting behind the scenes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Modern Switch Pattern Matching (Java 21 and Beyond)
&lt;/h2&gt;

&lt;p&gt;In earlier versions of Java, switch statements were limited to simple types like int, enum, or String. But starting with Java 21, pattern matching extends the power of switch by allowing you to match against an object's type directly. This results in cleaner, more expressive, and type-safe code.&lt;/p&gt;

&lt;p&gt;Here’s how type checking used to look without pattern matching:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (obj instanceof String) {
    String s = (String) obj;
    System.out.println("It's a String: " + s);
} else if (obj instanceof Integer) {
    Integer i = (Integer) obj;
    System.out.println("It's an Integer: " + i);
} else {
    System.out.println("Unknown type");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With switch and pattern matching, you can match types and extract variables in one step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;switch (obj) {  
    case String s  -&amp;gt; System.out.println("It's a String: " + s);  
    case Integer i -&amp;gt; System.out.println("It's an Integer: " + i);  
    default            -&amp;gt; System.out.println("Unknown type");  
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a significant improvement over the old way, where switch could only match constants:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;switch (number) {
    case 1 -&amp;gt; System.out.println("One");  // Old: only constants
    case 2 -&amp;gt; System.out.println("Two");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Going Beyond the Basics: Advanced Pattern Matching in Java
&lt;/h2&gt;

&lt;p&gt;Java 16 introduced basic pattern matching using instanceof, but Java 21 brought more sophisticated features. In this section, you’ll learn how to work with record patterns, nested record patterns, and guarded patterns in your Java code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Record Patterns
&lt;/h2&gt;

&lt;p&gt;Record patterns enable you to access the components of a record in a single, streamlined operation. This adds powerful deconstruction capabilities to Java, akin to those in functional programming languages.&lt;/p&gt;

&lt;p&gt;Using record patterns, you can deconstruct records—Java’s immutable data classes—directly within pattern-matching contexts such as instanceof checks and switch statements. Instead of manually extracting each field, you can retrieve all components at once.&lt;/p&gt;

&lt;p&gt;For example, consider the following usage of record patterns with instanceof:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;record Point(int x, int y) {}

// Without record patterns
if (p instanceof Point) {
    Point point = (Point) p;
    int x = point.x();
    int y = point.y();
    System.out.println(x + ", " + y);
}

// With record patterns
if (p instanceof Point(int x, int y)) {
    System.out.println(x + ", " + y);  // x and y are directly accessible
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Record patterns become even more powerful when combined with switch statements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Object obj = new Point(10, 20);

switch (obj) {
    case Point(int x, int y) when x &amp;gt; 0 &amp;amp;&amp;amp; y &amp;gt; 0 -&amp;gt; 
        System.out.println("Point in first quadrant: " + x + ", " + y);
    case Point(int x, int y) -&amp;gt; 
        System.out.println("Point elsewhere: " + x + ", " + y);
    default -&amp;gt; 
        System.out.println("Not a point");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Mastering Nested Record Patterns in Java
&lt;/h2&gt;

&lt;p&gt;Nested record patterns enable you to inspect records that include other records in a single, unified operation. This greatly simplifies handling complex, layered data.&lt;/p&gt;

&lt;p&gt;With nested patterns, you can simultaneously match and extract values from multiple levels within a data structure, allowing direct access to inner components without having to traverse each level individually.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Define two simple records
record Address(String city, String country) {}
record Person(String name, Address address) {}

// Create a person with an address
Person person = new Person("Rafael", new Address("Sao Paulo", "Brazil"));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For comparison, here’s how matching and extracting values looks without nested patterns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Multiple steps required
if (person instanceof Person) {
    String name = person.name();
    Address address = person.address();
    String city = address.city();
    String country = address.country();

    System.out.println(name + " lives in " + city + ", " + country);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this is how you’d write it using nested patterns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// One clean pattern
if (person instanceof Person(String name, Address(String city, String country))) {
    // All variables are immediately available
    System.out.println(name + " lives in " + city + ", " + country);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similar to standard record patterns, nested record patterns can also be applied within switch statements. In the first case of the example below, the condition matches only when the person’s country is "Ireland"; if not, the switch moves on to evaluate the other case.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;switch (person) {
    case Person(String name, Address(String city, "Ireland")) -&amp;gt;
        System.out.println(name + " lives in " + city + ", Ireland");
    case Person(String name, Address(String city, String country)) -&amp;gt;
        System.out.println(name + " lives in " + city + ", " + country);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The advantages are evident once more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reduced boilerplate: Nested record patterns enable extracting multiple components in one concise step.&lt;/li&gt;
&lt;li&gt;Enhanced readability: The pattern explicitly reveals the data being extracted.&lt;/li&gt;
&lt;li&gt;Type safety: The compiler guarantees that the extracted variables have the correct types.&lt;/li&gt;
&lt;li&gt;Seamless nested deconstruction: Complex data structures are handled effortlessly within a single pattern.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By using nested patterns, your code becomes clearer and easier to maintain—especially when dealing with intricate data like configuration settings, domain models, or API responses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Guarded Patterns (When Clauses) for Precise Matching
&lt;/h2&gt;

&lt;p&gt;Guarded patterns extend pattern matching by allowing you to include additional conditions using the when keyword. This lets you match objects not only by their type or structure but also based on the specific values they hold.&lt;/p&gt;

&lt;p&gt;As demonstrated below, a guarded pattern pairs a standard pattern with a Boolean expression to create more precise matches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;switch (obj) {
    case String s when s.length() &amp;gt; 5 -&amp;gt; 
        System.out.println("Long string: " + s);
    case String s -&amp;gt; 
        System.out.println("Short string: " + s);
    default -&amp;gt; 
        System.out.println("Not a string");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s how this code operates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Java first verifies whether the object fits the specified pattern (for example, whether it’s a String).&lt;/li&gt;
&lt;li&gt;If it matches, Java assigns the matched value to the variable (s).&lt;/li&gt;
&lt;li&gt;Then, Java evaluates the Boolean condition following the when clause.&lt;/li&gt;
&lt;li&gt;If the condition is satisfied, that case is executed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s explore a couple of practical examples. In the first example, a guarded pattern is used to check the content of a String:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;switch (input) {
    case String s when s.startsWith("http://") -&amp;gt; 
        System.out.println("HTTP URL");
    case String s when s.startsWith("https://") -&amp;gt; 
        System.out.println("Secure HTTPS URL");
    case String s -&amp;gt;     
        System.out.println("Not a URL");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here, we’re using the pattern with numbers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;switch (num) {
    case Integer i when i &amp;lt; 0 -&amp;gt; System.out.println("Negative");
    case Integer i when i == 0 -&amp;gt; System.out.println("Zero");
    case Integer i when i &amp;gt; 0 -&amp;gt; System.out.println("Positive");
    default -&amp;gt; System.out.println("Not an integer");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The advantages of guarded patterns include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More precise matching: Ability to target specific value-based conditions.&lt;/li&gt;
&lt;li&gt;Less code: Merges type checks and value validations into a single step.&lt;/li&gt;
&lt;li&gt;Improved readability: Expresses complex conditions clearly and succinctly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Leveraging Pattern Matching with Sealed Classes
&lt;/h2&gt;

&lt;p&gt;Using sealed classes together with pattern matching provides strong compile-time safety for your code. The examples in this section will demonstrate why this combination is truly transformative.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What are sealed classes?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Introduced in Java 17, sealed classes are classes that explicitly define which other classes can extend or implement them. Think of a sealed class as creating a “closed club” of related types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sealed interface JavaMascot permits Duke, Juggy { }
record Duke(String color, int yearIntroduced) implements JavaMascot { }
record Juggy(String color, boolean isFlying) implements JavaMascot { }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first glance, this code doesn’t seem to help much, but the real benefit starts when we try something like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;record Moby(String color, double tentacleLength) implements JavaMascot { } // Compilation error here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since the sealed interface JavaMascot allows only Duke and Juggy to implement it, the code above will fail to compile. This restriction prevents unauthorized classes from implementing the JavaMascot interface, thereby reducing the risk of bugs. In general, tighter constraints in your code lead to fewer opportunities for errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Leveraging Sealed Classes within Switch Constructs
&lt;/h2&gt;

&lt;p&gt;By combining sealed classes with switch in pattern matching, the compiler becomes fully aware of every possible subtype. This brings two key advantages: (1) the compiler can ensure that all subtypes are properly handled, and (2) because all cases are known upfront, there’s no need for a default case.&lt;/p&gt;

&lt;p&gt;Let’s see how sealed classes and switch collaborate in the example below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;String describeMascot(JavaMascot mascot) {
    return switch (mascot) {
        case Duke(String color, int yearIntroduced) -&amp;gt; 
            "Duke (" + color + ") from " + yearIntroduced;
        case Juggy(String color, boolean isFlying) -&amp;gt; 
            "Juggy (" + color + ")" + (isFlying ? " flying high" : "");
        // No default needed! The compiler knows these are all possibilities
    };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Java 23+ Enhancements: Primitive Type Pattern Matching
&lt;/h2&gt;

&lt;p&gt;Recent Java versions have introduced an exciting preview feature that allows pattern matching to work with primitive types, enabling compatibility checks directly in your code. We’ll explore this feature through several examples. Keep in mind, however, that since primitive type pattern matching is still in preview, it may be modified or removed in future Java releases. To run the examples below, make sure to enable preview features in your environment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;java --enable-preview YourClassNameHere
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What You Need to Know About Primitive Type Pattern Matching
&lt;/h2&gt;

&lt;p&gt;This feature allows us to check whether a value of one primitive type can be safely represented as another primitive type, and if so, bind that value to a new variable. The following example demonstrates this with integer compatibility in action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int count = 98;
if (count instanceof byte smallCount) {
    // This executes only if count fits in a byte's range
    System.out.println("Small enough: " + smallCount);
} else {
    System.out.println("Number too large for byte storage");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we’re checking if 98 can be stored in a byte. Since it’s between -128 and 127, the condition succeeds.&lt;/p&gt;

&lt;p&gt;Consider another example, this one evaluating decimal precision:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;double measurement = 17.5;
if (measurement instanceof float simpleMeasurement) {
    System.out.println("No precision loss: " + simpleMeasurement);
} else {
    System.out.println("Requires double precision");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This verifies if the double value can be represented as a float without precision loss.&lt;/p&gt;

&lt;p&gt;Here’s an example using primitive type pattern matching with text characters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int codePoint = 90;
if (codePoint instanceof char symbol) {
    System.out.println("This represents: '" + symbol + "'");
} else {
    System.out.println("Not a valid character code");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output from this code would be: This represents: ‘Z’, because 90 is the ASCII/Unicode value for Z.&lt;/p&gt;

&lt;p&gt;Finally, here’s a demonstration showing multiple type compatibility checks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void examineNumber(long input) {
    System.out.println("Examining: " + input);

    if (input instanceof byte b)
        System.out.println("Fits in a byte variable: " + b);    
    if (input instanceof short s)
        System.out.println("Fits in a short variable: " + s);
    if (input &amp;gt;= 0 &amp;amp;&amp;amp; input &amp;lt;= 65535 &amp;amp;&amp;amp; input instanceof char c)
        System.out.println("Represents character: '" + c + "'");
    if (input instanceof int i)
        System.out.println("Fits in an int variable: " + i);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When called with examineNumber(77), this code would output all four messages, including that 77 represents the character ‘M’.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Use Primitive Type Pattern Matching
&lt;/h2&gt;

&lt;p&gt;Primitive type pattern matching is especially useful in scenarios such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validating user input to ensure it falls within acceptable ranges.&lt;/li&gt;
&lt;li&gt;Safely converting between numeric types while minimizing data loss.&lt;/li&gt;
&lt;li&gt;Handling character encoding during text processing.&lt;/li&gt;
&lt;li&gt;Making numeric type conversions clearer and more reliable.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>java</category>
      <category>programming</category>
      <category>oop</category>
      <category>designpatterns</category>
    </item>
    <item>
      <title>Angular 20 + Micronaut</title>
      <dc:creator>anand jaisy</dc:creator>
      <pubDate>Mon, 30 Jun 2025 23:14:33 +0000</pubDate>
      <link>https://forem.com/anand_jaisy_2f7644a12001b/angular-20-micronaut-4ka0</link>
      <guid>https://forem.com/anand_jaisy_2f7644a12001b/angular-20-micronaut-4ka0</guid>
      <description>&lt;p&gt;In certain scenarios—particularly with smaller applications—it can be beneficial to serve both the frontend and backend from a single service. Instead of deploying the frontend (e.g., an Angular application) and the backend (e.g., a REST API) on separate servers, a more efficient approach may be to consolidate them into a single, lightweight deployment. This strategy helps reduce infrastructure complexity and minimizes the application’s footprint.&lt;/p&gt;

&lt;p&gt;Micronaut, with its fast startup time and minimal resource usage, pairs well with Angular for such use cases.&lt;/p&gt;

&lt;p&gt;In this blog post, we’ll explore how to serve an Angular application directly from a Micronaut backend, creating a unified deployment that is simple, efficient, and well-suited for small to medium-sized projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Setting Up the Micronaut app
&lt;/h2&gt;

&lt;p&gt;Head over to &lt;a href="https://micronaut.io/launch/" rel="noopener noreferrer"&gt;Micronaut launch&lt;/a&gt;, just create a basic application&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%2Fgqrska3e4d0knn5xoh4t.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%2Fgqrska3e4d0knn5xoh4t.png" alt="Micronaut app start" width="800" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we look into the dependency, its a basic minimal Micronaut app&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plugins {
    id("io.micronaut.application") version "4.5.4"
    id("com.gradleup.shadow") version "8.3.7"
    id("io.micronaut.aot") version "4.5.4"
}

version = "0.1"
group = "micronaut.angular"

repositories {
    mavenCentral()
}

dependencies {
    annotationProcessor("io.micronaut:micronaut-http-validation")
    annotationProcessor("io.micronaut.serde:micronaut-serde-processor")
    implementation("io.micronaut.serde:micronaut-serde-jackson")
    compileOnly("io.micronaut:micronaut-http-client")
    runtimeOnly("ch.qos.logback:logback-classic")
    testImplementation("io.micronaut:micronaut-http-client")
}


application {
    mainClass = "micronaut.angular.Application"
}
java {
    sourceCompatibility = JavaVersion.toVersion("21")
    targetCompatibility = JavaVersion.toVersion("21")
}


graalvmNative.toolchainDetection = false

micronaut {
    runtime("netty")
    testRuntime("junit5")
    processing {
        incremental(true)
        annotations("micronaut.angular.*")
    }
    aot {
        // Please review carefully the optimizations enabled below
        // Check https://micronaut-projects.github.io/micronaut-aot/latest/guide/ for more details
        optimizeServiceLoading = false
        convertYamlToJava = false
        precomputeOperations = true
        cacheEnvironment = true
        optimizeClassLoading = true
        deduceEnvironment = true
        optimizeNetty = true
        replaceLogbackXml = true
    }
}

tasks.named&amp;lt;io.micronaut.gradle.docker.NativeImageDockerfile&amp;gt;("dockerfileNative") {
    jdkVersion = "21"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that our backend service is set up and ready, it's time to focus on the frontend. In this section, we'll set up an Angular application using the standard Angular CLI, which provides a streamlined and efficient way to scaffold and manage Angular projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Angular setup
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Install the Angular CLI
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g @angular/cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create a workspace and initial application
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng new ui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;We can select select any choice, however we will stick to SCSS.&lt;/li&gt;
&lt;/ul&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%2Fa4eyfg9282jww7a7zzmc.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%2Fa4eyfg9282jww7a7zzmc.png" alt="css" width="800" height="172"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We don't want SSR, our choice will be N&lt;/p&gt;
&lt;/blockquote&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%2F5pizo6bp1l98mmdvr633.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%2F5pizo6bp1l98mmdvr633.png" alt="ssr" width="800" height="72"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The angular CLI will install few dependencies&lt;/li&gt;
&lt;/ul&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%2Fjafox6iaj4idio9243kg.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%2Fjafox6iaj4idio9243kg.png" alt="Dependencies" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Once the dependencies are install, we can see the below project structure&lt;/li&gt;
&lt;/ul&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%2F4al1z3dz75be488auyyi.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%2F4al1z3dz75be488auyyi.png" alt="UI project" width="369" height="806"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd new ui

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Run the application
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng serve --open
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enter the URL &lt;a href="http://localhost:4200/" rel="noopener noreferrer"&gt;http://localhost:4200/&lt;/a&gt; in the browser and we will the angular app is running&lt;/p&gt;

&lt;h2&gt;
  
  
  Development Workflow
&lt;/h2&gt;

&lt;p&gt;During development, it's perfectly fine (and often preferable) to run Angular and Spring Boot as separate applications:&lt;/p&gt;

&lt;p&gt;Angular CLI: Serves the frontend on &lt;a href="http://localhost:4200" rel="noopener noreferrer"&gt;http://localhost:4200&lt;/a&gt; with hot-reload for fast development.&lt;/p&gt;

&lt;p&gt;Micronaut: Runs the backend API on &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This separation allows for:&lt;br&gt;
✔ Faster frontend iterations (thanks to Angular's live reload)&lt;br&gt;
✔ Independent debugging of backend APIs&lt;br&gt;
✔ Mock API responses during early development&lt;/p&gt;
&lt;h2&gt;
  
  
  The Production Challenge
&lt;/h2&gt;

&lt;p&gt;In production, we typically want to serve both applications as a single unit for:&lt;br&gt;
✔ Simplified deployment&lt;br&gt;
✔ Reduced cross-origin issues (CORS)&lt;br&gt;
✔ Better performance (serving static assets directly from the backend)&lt;/p&gt;
&lt;h2&gt;
  
  
  Configuring Angular to Deploy as Spring Boot Static Content for production
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Understanding the Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By default, Angular builds to /dist/ui, but we need it to output directly to Spring Boot's static resources folder where it can be automatically served. Here's how to make this work seamlessly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Step 1: Modify Angular's Output Path
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"architect": {
  "build": {
    "builder": "@angular-devkit/build-angular:application",
    "options": {
      "outputPath": "dist/ui",
    },
}
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Lets change the "outputPath": "dist/ui" to the Micronaut resource directory&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"architect": {
  "build": {
    "builder": "@angular-devkit/build-angular:application",
    "options": {
      "outputPath": "../src/main/resources/static",  // Changed from "dist/ui"
      "index": "src/index.html",
      // ... rest of your config
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;"outputPath": "../src/main/resources/static"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Since now the outputPath is pointing to the Micronaut resources, we can run angular CLI command to build the files, we must point to the root directory of the angular app in our case ui&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Step 2: Build and Verify
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng build --configuration production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will see the below logs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; ng build --configuration production
Initial chunk files   | Names         |  Raw size | Estimated transfer size
main-3BERZHFR.js      | main          | 208.01 kB |                56.74 kB
polyfills-FFHMD2TL.js | polyfills     |  34.52 kB |                11.28 kB
styles-5INURTSO.css   | styles        |   0 bytes |                 0 bytes

                      | Initial total | 242.53 kB |                68.02 kB

Application bundle generation complete. [2.279 seconds]

Output location: /Users/san/project/Sample/AngularTest/src/main/resources/static
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the output location here Output location: /Users/san/project/Sample/AngularTest/src/main/resources/static. The files are been output the the Micronaut resources directory.&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%2F9793o0u6xaxmo03fuuh0.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%2F9793o0u6xaxmo03fuuh0.png" alt="directory" width="552" height="853"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Run the Micronaut app, and head over to the browser &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt; we will see there is no angular app.&lt;/p&gt;

&lt;p&gt;If you have looked closely to the &lt;code&gt;resources &amp;gt;&amp;gt; static directory&lt;/code&gt;, there is browser directory is created by angular build command, and if we look into that directory we can see the index.html and .js file. For angular to work that needs to be on outside of the browser directory, because spring doesn't know anything about browser directory.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Solution 1 - We can move those files outside of the browser
Now, if we run our Micronaut app, and navigate to &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt; we will see angular application&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Well this works right, hmmmm not that effective&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The problem with this solution is that we need to manually move the files, lets see how we can fix this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Better Solution&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; "outputPath": {
              "base": "../src/main/resources/static",
              "browser": ""
            },
            "deleteOutputPath": false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Include browser ="" to generate file inside the static directory and don't delete other files by including "deleteOutputPath": false,. Now if we run the command ng build --configuration production we can see all the files are generated within static&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add a new component&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng g c Home // This will generate new Home Component
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Include this component in the router section&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Routes } from '@angular/router';
import {HomeComponent} from './home/home.component';

export const routes: Routes = [{ path: 'home', component: HomeComponent },];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For local development we can reply on ng server. The routing will work on &lt;a href="http://localhost:4200/home" rel="noopener noreferrer"&gt;http://localhost:4200/home&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For Micronaut to include the router we need to build again&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng build --configuration production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we navigate to &lt;a href="http://localhost:8080/home" rel="noopener noreferrer"&gt;http://localhost:8080/home&lt;/a&gt; we will face an issue as 404 not found&lt;/p&gt;

&lt;p&gt;To fix this we have to do configuration SPA routing&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Controller("/")
public class SpaController {
    @Inject
    ResourceResolver res;

    @Get("/{path:(?!.*\\.).*}")
    @Produces(MediaType.TEXT_HTML)
    public HttpResponse&amp;lt;?&amp;gt; refresh(@PathVariable String path) {
        StreamedFile indexFile = new StreamedFile(res.getResource("classpath:public/index.html").get());
        return HttpResponse.ok(indexFile);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Application.properties&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;micronaut.router.static-resources.default.enabled=true
micronaut.router.static-resources.default.mapping=/**
micronaut.router.static-resources.default.paths=classpath:public
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Development vs Production Workflow
&lt;/h2&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%2Fvj9r2py27rpsnrv33r7j.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%2Fvj9r2py27rpsnrv33r7j.png" alt="dev vs prod" width="800" height="178"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now if we run the spring boot app and navigate to &lt;a href="http://localhost:8080/home" rel="noopener noreferrer"&gt;http://localhost:8080/home&lt;/a&gt; we will see our home component.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For development, configure Angular's proxy to avoid CORS issues:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/proxy.conf.json
{
  "/api": {
    "target": "http://localhost:8080",
    "secure": false
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;The Best of Both Worlds&lt;br&gt;
By configuring Angular to build directly into Micronaut  static resources folder, we've created a powerful full-stack solution that:&lt;/p&gt;

&lt;p&gt;✔ Simplifies Deployment - A single JAR contains both frontend and backend&lt;br&gt;
✔ Improves Performance - Static assets are served efficiently by the embedded Tomcat server&lt;br&gt;
✔ Maintains Flexibility - Keep separate dev servers during development while unifying for production&lt;/p&gt;

</description>
      <category>angular</category>
      <category>micronaut</category>
      <category>java</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How small can Java app on the container</title>
      <dc:creator>anand jaisy</dc:creator>
      <pubDate>Tue, 24 Jun 2025 09:35:49 +0000</pubDate>
      <link>https://forem.com/anand_jaisy_2f7644a12001b/how-small-can-java-app-on-container-ml6</link>
      <guid>https://forem.com/anand_jaisy_2f7644a12001b/how-small-can-java-app-on-container-ml6</guid>
      <description>&lt;p&gt;If you're looking to build lightning-fast Java applications with minimal container footprint, Micronaut + GraalVM is the perfect combination. In this post, we'll walk through creating a Micronaut app, containerizing it, and optimizing it with native images and distroless containers.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛠️ Getting Started with Micronaut
&lt;/h2&gt;

&lt;p&gt;You can quickly generate a Micronaut project using the &lt;a href="https://micronaut.io/launch/" rel="noopener noreferrer"&gt;Micronaut Launch tool&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fin36i73twa8ib06mgggs.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%2Fin36i73twa8ib06mgggs.png" alt="memory on base" width="800" height="313"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or via the CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mn create-app --build=gradle_kotlin --jdk=21 --lang=java --test=junit --features=openapi,swagger-ui,management,gcp-logging fete.bird.container-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./gradlew run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; __  __ _                                  _   
|  \/  (_) ___ _ __ ___  _ __   __ _ _   _| |_ 
| |\/| | |/ __| '__/ _ \| '_ \ / _` | | | | __|
| |  | | | (__| | | (_) | | | | (_| | |_| | |_ 
|_|  |_|_|\___|_|  \___/|_| |_|\__,_|\__,_|\__|
08:22:52.715 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 312ms. Server Running: http://localhost:8080
&amp;lt;==========---&amp;gt; 80% EXECUTING [13s]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🚀 Startup: ~312ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🐳 Containerizing the App with Docker
&lt;/h2&gt;

&lt;p&gt;Micronaut provides convenient Gradle tasks to build Docker artifacts. Let's generate the standard Dockerfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./gradlew dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll find the Dockerfile at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Build 👉🏻➔ docker 👉🏻➔ main 👉🏻➔ Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s what it looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM eclipse-temurin:21-jre
WORKDIR /home/app
COPY --link layers/libs /home/app/libs
COPY --link layers/app /home/app/
COPY --link layers/resources /home/app/resources
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/home/app/application.jar"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we have notice in the Dockerfile, we can see a COPY command with --link &lt;strong&gt;layers&lt;/strong&gt;. This mean we need to create layers directory. OHHH how can I created, don't worry micronaut gradle has all plugin. Lets run another Gradle task to build those directory&lt;/p&gt;

&lt;p&gt;Now, generate the necessary layer files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./gradlew buildLayers
&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F773dhchw1l19ttn3mfvu.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%2F773dhchw1l19ttn3mfvu.png" alt="uploads for scatch" width="800" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Build the Docker image: &lt;/p&gt;

&lt;p&gt;Navigate to the Dockerfile &lt;/p&gt;

&lt;p&gt;~/project/Sample/container-demo/build/docker/main &lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker buildx build -f Dockerfile -t micronuat-temurin.21 .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets check the docker image in docker&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker image ls
&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsc1tgv3h5no6nbl9n9ql.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%2Fsc1tgv3h5no6nbl9n9ql.png" alt="uploads john" width="800" height="67"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🧱 Image: eclipse-temurin:21-jre
🚀 Startup: ~340ms
📦 Container size: 337MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run --rm -p 8080:8080 micronuat-temurin.21
 __  __ _                                  _   
|  \/  (_) ___ _ __ ___  _ __   __ _ _   _| |_ 
| |\/| | |/ __| '__/ _ \| '_ \ / _` | | | | __|
| |  | | | (__| | | (_) | | | | (_| | |_| | |_ 
|_|  |_|_|\___|_|  \___/|_| |_|\__,_|\__,_|\__|
22:44:36.206 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 340ms. Server Running: http://5a2c1732c6ad:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Can we make it better, can Java be better for container ?? Hell yeah, let explore native with GRAALVM&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚡ Going Native with GraalVM
&lt;/h2&gt;

&lt;p&gt;Lets run the gradle task &lt;code&gt;./gradlew dockerfileNative&lt;/code&gt;. This will create a DockerfileNative file under&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Build 👉🏻➔ docker 👉🏻➔ native-main 👉🏻➔ DockerfileNative
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM ghcr.io/graalvm/native-image-community:21-ol9 AS graalvm
WORKDIR /home/app
COPY --link layers/libs /home/app/libs
COPY --link layers/app /home/app/
COPY --link layers/resources /home/app/resources
RUN mkdir /home/app/config-dirs
RUN mkdir -p /home/app/config-dirs/generateResourcesConfigFile
RUN mkdir -p /home/app/config-dirs/ch.qos.logback.contrib/logback-json-classic/0.1.5
RUN mkdir -p /home/app/config-dirs/ch.qos.logback/logback-classic/1.4.9
RUN mkdir -p /home/app/config-dirs/org.apache.httpcomponents/httpclient/4.5.14
RUN mkdir -p /home/app/config-dirs/com.google.protobuf/protobuf-java-util/3.21.12
RUN mkdir -p /home/app/config-dirs/io.netty/netty-common/4.1.115.Final
RUN mkdir -p /home/app/config-dirs/io.netty/netty-transport/4.1.115.Final
COPY --link config-dirs/generateResourcesConfigFile /home/app/config-dirs/generateResourcesConfigFile
COPY --link config-dirs/ch.qos.logback.contrib/logback-json-classic/0.1.5 /home/app/config-dirs/ch.qos.logback.contrib/logback-json-classic/0.1.5
COPY --link config-dirs/ch.qos.logback/logback-classic/1.4.9 /home/app/config-dirs/ch.qos.logback/logback-classic/1.4.9
COPY --link config-dirs/org.apache.httpcomponents/httpclient/4.5.14 /home/app/config-dirs/org.apache.httpcomponents/httpclient/4.5.14
COPY --link config-dirs/com.google.protobuf/protobuf-java-util/3.21.12 /home/app/config-dirs/com.google.protobuf/protobuf-java-util/3.21.12
COPY --link config-dirs/io.netty/netty-common/4.1.115.Final /home/app/config-dirs/io.netty/netty-common/4.1.115.Final
COPY --link config-dirs/io.netty/netty-transport/4.1.115.Final /home/app/config-dirs/io.netty/netty-transport/4.1.115.Final
RUN native-image --exclude-config .*/libs/netty-buffer-4.1.119.Final.jar ^/META-INF/native-image/.* --exclude-config .*/libs/netty-common-4.1.119.Final.jar ^/META-INF/native-image/.* --exclude-config .*/libs/netty-codec-http-4.1.119.Final.jar ^/META-INF/native-image/.* --exclude-config .*/libs/netty-transport-4.1.119.Final.jar ^/META-INF/native-image/.* --exclude-config .*/libs/netty-codec-http2-4.1.119.Final.jar ^/META-INF/native-image/.* --exclude-config .*/libs/netty-handler-4.1.119.Final.jar ^/META-INF/native-image/.* -cp /home/app/libs/*.jar:/home/app/resources:/home/app/application.jar --no-fallback -o application -H:ConfigurationFileDirectories=/home/app/config-dirs/generateResourcesConfigFile,/home/app/config-dirs/ch.qos.logback.contrib/logback-json-classic/0.1.5,/home/app/config-dirs/ch.qos.logback/logback-classic/1.4.9,/home/app/config-dirs/org.apache.httpcomponents/httpclient/4.5.14,/home/app/config-dirs/com.google.protobuf/protobuf-java-util/3.21.12,/home/app/config-dirs/io.netty/netty-codec-http/4.1.80.Final,/home/app/config-dirs/io.netty/netty-common/4.1.115.Final,/home/app/config-dirs/io.netty/netty-buffer/4.1.80.Final,/home/app/config-dirs/io.netty/netty-transport/4.1.115.Final,/home/app/config-dirs/io.netty/netty-handler/4.1.80.Final,/home/app/config-dirs/io.netty/netty-codec-http2/4.1.80.Final fete.bird.Application
FROM cgr.dev/chainguard/wolfi-base:latest
EXPOSE 8080
COPY --link --from=graalvm /home/app/application /app/application
ENTRYPOINT ["/app/application"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets run few task to generate the directory with files&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./gradlew buildNativeLayers
./gradlew dockerPrepareContext
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets build the docker image&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker buildx build -f DockerfileNative -t micronuat-native-graal .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets check the image&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker image ls
&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2egazb2cyxzq9d13yn5t.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%2F2egazb2cyxzq9d13yn5t.png" alt="john" width="800" height="82"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run --rm -p 8080:8080 micronuat-native-graal
 __  __ _                                  _   
|  \/  (_) ___ _ __ ___  _ __   __ _ _   _| |_ 
| |\/| | |/ __| '__/ _ \| '_ \ / _` | | | | __|
| |  | | | (__| | | (_) | | | | (_| | |_| | |_ 
|_|  |_|_|\___|_|  \___/|_| |_|\__,_|\__,_|\__|
22:57:32.389 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 12ms. Server Running: http://388dc34c3d3d:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🧱 Image: ghcr.io/graalvm/native-image-community:21-ol9
🚀 Startup: ~12ms
📦 Container size: 82.5MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  📦 Optimizing Further with Distroless Containers
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tasks.named&amp;lt;io.micronaut.gradle.docker.NativeImageDockerfile&amp;gt;("dockerfileNative") {
    jdkVersion = "21"
    baseImage("gcr.io/distroless/static-debian12")
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create again NativeImageDockerfile&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./gradlew dockerfileNative
./gradlew buildNativeLayers
./gradlew dockerPrepareContext
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM ghcr.io/graalvm/native-image-community:21-ol9 AS graalvm
WORKDIR /home/app
COPY --link layers/libs /home/app/libs
COPY --link layers/app /home/app/
COPY --link layers/resources /home/app/resources
RUN mkdir /home/app/config-dirs
RUN mkdir -p /home/app/config-dirs/generateResourcesConfigFile
RUN mkdir -p /home/app/config-dirs/ch.qos.logback.contrib/logback-json-classic/0.1.5
RUN mkdir -p /home/app/config-dirs/ch.qos.logback/logback-classic/1.4.9
RUN mkdir -p /home/app/config-dirs/org.apache.httpcomponents/httpclient/4.5.14
RUN mkdir -p /home/app/config-dirs/com.google.protobuf/protobuf-java-util/3.21.12
RUN mkdir -p /home/app/config-dirs/io.netty/netty-common/4.1.115.Final
RUN mkdir -p /home/app/config-dirs/io.netty/netty-transport/4.1.115.Final
COPY --link config-dirs/generateResourcesConfigFile /home/app/config-dirs/generateResourcesConfigFile
COPY --link config-dirs/ch.qos.logback.contrib/logback-json-classic/0.1.5 /home/app/config-dirs/ch.qos.logback.contrib/logback-json-classic/0.1.5
COPY --link config-dirs/ch.qos.logback/logback-classic/1.4.9 /home/app/config-dirs/ch.qos.logback/logback-classic/1.4.9
COPY --link config-dirs/org.apache.httpcomponents/httpclient/4.5.14 /home/app/config-dirs/org.apache.httpcomponents/httpclient/4.5.14
COPY --link config-dirs/com.google.protobuf/protobuf-java-util/3.21.12 /home/app/config-dirs/com.google.protobuf/protobuf-java-util/3.21.12
COPY --link config-dirs/io.netty/netty-common/4.1.115.Final /home/app/config-dirs/io.netty/netty-common/4.1.115.Final
COPY --link config-dirs/io.netty/netty-transport/4.1.115.Final /home/app/config-dirs/io.netty/netty-transport/4.1.115.Final
RUN native-image --exclude-config .*/libs/netty-buffer-4.1.119.Final.jar ^/META-INF/native-image/.* --exclude-config .*/libs/netty-common-4.1.119.Final.jar ^/META-INF/native-image/.* --exclude-config .*/libs/netty-codec-http-4.1.119.Final.jar ^/META-INF/native-image/.* --exclude-config .*/libs/netty-transport-4.1.119.Final.jar ^/META-INF/native-image/.* --exclude-config .*/libs/netty-codec-http2-4.1.119.Final.jar ^/META-INF/native-image/.* --exclude-config .*/libs/netty-handler-4.1.119.Final.jar ^/META-INF/native-image/.* -cp /home/app/libs/*.jar:/home/app/resources:/home/app/application.jar --no-fallback -o application -H:ConfigurationFileDirectories=/home/app/config-dirs/generateResourcesConfigFile,/home/app/config-dirs/ch.qos.logback.contrib/logback-json-classic/0.1.5,/home/app/config-dirs/ch.qos.logback/logback-classic/1.4.9,/home/app/config-dirs/org.apache.httpcomponents/httpclient/4.5.14,/home/app/config-dirs/com.google.protobuf/protobuf-java-util/3.21.12,/home/app/config-dirs/io.netty/netty-codec-http/4.1.80.Final,/home/app/config-dirs/io.netty/netty-common/4.1.115.Final,/home/app/config-dirs/io.netty/netty-buffer/4.1.80.Final,/home/app/config-dirs/io.netty/netty-transport/4.1.115.Final,/home/app/config-dirs/io.netty/netty-handler/4.1.80.Final,/home/app/config-dirs/io.netty/netty-codec-http2/4.1.80.Final fete.bird.Application -H:+StaticExecutableWithDynamicLibC
FROM gcr.io/distroless/static-debian12
EXPOSE 8080
COPY --link --from=graalvm /home/app/application /app/application
ENTRYPOINT ["/app/application"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets build the docker image&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker buildx build -f DockerfileNative -t micronuat-native-graal .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets check the image&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker image ls
&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh0tl69k82wyr2zkwknxb.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%2Fh0tl69k82wyr2zkwknxb.png" alt="base image" width="800" height="102"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run --rm -p 8080:8080 micronuat-native-graal
 __  __ _                                  _   
|  \/  (_) ___ _ __ ___  _ __   __ _ _   _| |_ 
| |\/| | |/ __| '__/ _ \| '_ \ / _` | | | | __|
| |  | | | (__| | | (_) | | | | (_| | |_| | |_ 
|_|  |_|_|\___|_|  \___/|_| |_|\__,_|\__,_|\__|
22:57:32.389 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 10ms. Server Running: http://388dc34c3d3d:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🧱 Image: ghcr.io/graalvm/native-image-community:21-ol9
🚀 Startup: ~10ms
📦 Container size: 70.6MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Can you go further - YES &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Running a Fully Static Application — In an Empty Container
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🧱 Image: scratch
🚀 Startup: ~8ms
📦 Container size: 69.2MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Going Extreme — UPX Compression
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🧱 Image: scratch
🚀 Startup: ~6ms
📦 Container size: 22.3MB
&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsmmc3w6uxa686exb9kr5.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%2Fsmmc3w6uxa686exb9kr5.png" alt="Memory consumption" width="800" height="341"&gt;&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1rbsy96wr4pcr19ym3mu.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%2F1rbsy96wr4pcr19ym3mu.png" alt="startup time" width="800" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🧵 Conclusion
&lt;/h2&gt;

&lt;p&gt;Micronaut combined with GraalVM unlocks blazing-fast Java apps with minimal startup time and memory usage—ideal for cloud-native and serverless deployments. With the help of native compilation and container optimizations, Java is now a top-tier choice for microservices at scale.&lt;/p&gt;

&lt;p&gt;More details here - &lt;a href="https://medium.com/graalvm/from-jit-to-native-path-to-efficient-java-containers-d81221418c39" rel="noopener noreferrer"&gt;https://medium.com/graalvm/from-jit-to-native-path-to-efficient-java-containers-d81221418c39&lt;/a&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>graalvm</category>
      <category>jdk</category>
      <category>containers</category>
    </item>
    <item>
      <title>Exploring Firestore with Micronaut and Java 21: A Scalable NoSQL Solution for Modern Applications</title>
      <dc:creator>anand jaisy</dc:creator>
      <pubDate>Tue, 17 Jun 2025 21:50:28 +0000</pubDate>
      <link>https://forem.com/anand_jaisy_2f7644a12001b/exploring-firestore-with-micronaut-and-java-21-a-scalable-nosql-solution-for-modern-applications-ea2</link>
      <guid>https://forem.com/anand_jaisy_2f7644a12001b/exploring-firestore-with-micronaut-and-java-21-a-scalable-nosql-solution-for-modern-applications-ea2</guid>
      <description>&lt;p&gt;As we continue to develop and deploy a growing number of microservices, one critical consideration is the choice of data storage. While traditional relational databases such as PostgreSQL, MS SQL Server, and MySQL offer robust capabilities, they can become expensive and complex to scale in serverless environments like Google Cloud Run, AWS Lambda, or Azure Functions.&lt;/p&gt;

&lt;p&gt;Serverless architectures offer inherent scalability, and managing relational database scaling in such environments often introduces additional operational overhead and cost. That said, the decision to use a relational database (RDBMS) versus a NoSQL solution should always be driven by the specific business and data requirements.&lt;/p&gt;

&lt;p&gt;In this blog post, we explore Google Cloud &lt;a href="https://cloud.google.com/products/firestore?hl=en" rel="noopener noreferrer"&gt;Firestore&lt;/a&gt;, a fully managed, serverless NoSQL document database built for automatic scaling, high performance, and ease of application development. &lt;a href="https://cloud.google.com/products/firestore?hl=en" rel="noopener noreferrer"&gt;Firestore &lt;/a&gt;is particularly well-suited for modern applications, especially those involving AI and ML workloads.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cloud.google.com/products/firestore?hl=en" rel="noopener noreferrer"&gt;Firestore &lt;/a&gt;integrates seamlessly with tools like LangChain and LlamaIndex, supporting patterns such as:&lt;/p&gt;

&lt;p&gt;Document loaders for managing and storing structured content,&lt;/p&gt;

&lt;p&gt;Vector stores for similarity and semantic search,&lt;/p&gt;

&lt;p&gt;Memory modules for use cases like chat history retention.&lt;/p&gt;

&lt;p&gt;Additionally, Firestore provides out-of-the-box extensions that simplify integration with popular AI services, enabling features such as automated embedding generation, language translation, and image classification with minimal configuration.&lt;/p&gt;

&lt;p&gt;In this article, we'll walk through how to use &lt;a href="https://cloud.google.com/products/firestore?hl=en" rel="noopener noreferrer"&gt;Firestore &lt;/a&gt;on the server side, using &lt;a href="https://micronaut.io/" rel="noopener noreferrer"&gt;Micronaut &lt;/a&gt;and Java 21 as our development stack. Let’s dive into how &lt;a href="https://cloud.google.com/products/firestore?hl=en" rel="noopener noreferrer"&gt;Firestore &lt;/a&gt;can help you build scalable, AI-powered applications with ease.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;OOPS, we’ve had enough theory—let’s dive into some code and see how Micronaut can help us integrate with Firestore and build a robust document storage solution.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Google Cloud project with Firestore enabled.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://micronaut.io/launch/" rel="noopener noreferrer"&gt;Micronaut &lt;/a&gt;CLI or a Micronaut project setup.&lt;/li&gt;
&lt;li&gt;Google Cloud SDK and credentials for Firestore access.&lt;/li&gt;
&lt;li&gt;Java 21 and Maven/Gradle for dependency management.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Getting Started with Firestore in Micronaut (Java 21)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Micronaut is a modern, lightweight framework optimized for building microservices and serverless applications with fast startup time and low memory usage—ideal for cloud-native deployments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Bootstrap the Project&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Head over to the &lt;a href="https://micronaut.io/launch/" rel="noopener noreferrer"&gt;Micronaut &lt;/a&gt;Launch site or use the Micronaut CLI to generate your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mn create-app \
  --build=gradle_kotlin \
  --jdk=21 \
  --lang=java \
  --test=junit \
  --features=micronaut-aot,graalvm,management,gcp-logging \
  san.jaisy.firestore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s break down the features we’ve included:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;micronaut-aot:&lt;/strong&gt; Enables ahead-of-time (AOT) compilation for faster startup and smaller footprint—great for serverless.&lt;br&gt;
&lt;strong&gt;graalvm:&lt;/strong&gt; Adds support for native image generation with GraalVM.&lt;br&gt;
&lt;strong&gt;management:&lt;/strong&gt; Provides production-ready features such as health checks and metrics.&lt;br&gt;
&lt;strong&gt;gcp-logging:&lt;/strong&gt; Integrates with Google Cloud Logging for easy observability in GCP environments.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Congratulations! You’ve just generated a project that’s cloud-native, AI-friendly, and 100% cooler than other framwork.&lt;/p&gt;

&lt;p&gt;Now let’s put that shiny new app to work and get Firestore talking to it—like two old friends reconnecting over JSON.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Client libraries&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Set up &lt;a href="https://cloud.google.com/docs/authentication/application-default-credentials" rel="noopener noreferrer"&gt;Application Default Credentials (ADC)&lt;/a&gt; in your local environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gcloud init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're using a local shell, then create local authentication credentials for your user account:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gcloud auth application-default login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;NOTE: You don't need to do this if you're using Cloud Shell.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Exploring the build.gradle.kts File&lt;/strong&gt;&lt;br&gt;
Once you've generated your Micronaut project, the next stop is your build.gradle.kts file. Here's what it should look like by default:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plugins {
    id("io.micronaut.application") version "4.5.3"
    id("com.gradleup.shadow") version "8.3.6"
    id("io.micronaut.aot") version "4.5.3"
}
version = "0.1"
group = "san.jaisy"
repositories {
    mavenCentral()
}
dependencies {
    annotationProcessor("io.micronaut:micronaut-http-validation")
    annotationProcessor("io.micronaut.serde:micronaut-serde-processor")
    implementation("io.micronaut:micronaut-management")
    implementation("io.micronaut.gcp:micronaut-gcp-logging")
    implementation("io.micronaut.serde:micronaut-serde-jackson")
    compileOnly("io.micronaut:micronaut-http-client")
    runtimeOnly("ch.qos.logback:logback-classic")
    testImplementation("io.micronaut:micronaut-http-client")
}
application {
    mainClass = "san.jaisy.Application"
}
java {
    sourceCompatibility = JavaVersion.toVersion("21")
    targetCompatibility = JavaVersion.toVersion("21")
}
graalvmNative.toolchainDetection = false
micronaut {
    runtime("netty")
    testRuntime("junit5")
    processing {
        incremental(true)
        annotations("san.jaisy.*")
    }
    aot {
        // Please review carefully the optimizations enabled below
        // Check https://micronaut-projects.github.io/micronaut-aot/latest/guide/ for more details
        optimizeServiceLoading = false
        convertYamlToJava = false
        precomputeOperations = true
        cacheEnvironment = true
        optimizeClassLoading = true
        deduceEnvironment = true
        optimizeNetty = true
        replaceLogbackXml = true
    }
}
tasks.named&amp;lt;io.micronaut.gradle.docker.NativeImageDockerfile&amp;gt;("dockerfileNative") {
    jdkVersion = "21"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4:  Wait a Minute… Where’s Firestore?&lt;/strong&gt;&lt;br&gt;
If you’ve been following along, you might have noticed that while we've set up a solid foundation, we haven’t actually added Firestore yet. 😅&lt;/p&gt;

&lt;p&gt;Let’s fix that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;implementation("com.google.cloud:google-cloud-firestore:3.31.6")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🎉 And just like that, your app is Firestore-ready... or is it?&lt;br&gt;
Well, not quite. While the library is in place, we still need to configure the Firestore client, authenticate with GCP, and write some actual code to make it do something meaningful.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 5:  Coding time 👨‍💻👩‍💻 👨‍💻👩‍💻 🔥🔥 🎉🎉&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;📡 Controller: Exposing the Feedback API&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Controller("v1/firestore")
@ExecuteOn(TaskExecutors.BLOCKING)
public class FirestoreController {
    private final IFirestoreService firestoreService;

    public OutlookController(IFirestoreService firestoreService) {
        this.firestoreService = firestoreService;
    }

    @Post("/feedback")
    public HttpResponse&amp;lt;?&amp;gt; feedback(@Valid @Body UserFeedbackRequest request) {
        var result = firestoreService.create(request,httpRequest);
        return result.match(success -&amp;gt; HttpResponse.ok(result.value), () -&amp;gt; HttpResponse.serverError());
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📝 This controller exposes a POST /v1/firestore/feedback endpoint to accept user feedback.&lt;/p&gt;

&lt;p&gt;🧾 DTO: Validating Feedback Requests&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Serdeable
public record UserFeedbackRequest(@NotNull String requestId,
                                  @NotNull String itemId,
                                  @Size(min = 10)  String feedback,
                                  FeedbackType feedbackType) {
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🎯 This record handles incoming feedback. We apply validation annotations (@NotNull, @Size) so invalid data is caught before hitting the database.&lt;/p&gt;

&lt;p&gt;🏭 Factory: Configuring Firestore Client&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Factory
public class FirestoreFactory {
    @Singleton
    public Firestore firestore(GcpConfig gcpConfig) throws IOException {
        return FirestoreOptions.getDefaultInstance().toBuilder()
                .setProjectId(gcpConfig.project())
                .setCredentials(GoogleCredentials.getApplicationDefault())
                .build()
                .getService();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🔧 This factory sets up a singleton Firestore client using the FirestoreOptions builder.&lt;/p&gt;

&lt;p&gt;🧩 Interface: Contract for the Firestore Service&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public sealed interface IFirestoreService permits FirestoreService {
    Result&amp;lt;Response&amp;lt;UserFeedbackRequest&amp;gt;&amp;gt; create(UserFeedbackRequest userFeedbackRequest, HttpRequest&amp;lt;?&amp;gt; httpRequest);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💾 Service: Writing Data to Firestore&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Singleton
public record FirestoreService(Firestore firestore) implements IFirestoreService {
    private static final Logger LOG = Logger.getLogger(FirestoreService.class.getName());
    @Override
    public Result&amp;lt;Response&amp;lt;UserFeedbackRequest&amp;gt;&amp;gt; create(UserFeedbackRequest request) {
        // Store all feedback in the same top-level collection
        DocumentReference docRef = firestore.collection("firestore").document("feedback");

        Map&amp;lt;String, Object&amp;gt; data = new HashMap&amp;lt;&amp;gt;();
        data.put("RequestId", requestId);
        data.put("ItemId", request.itemId());
        data.put("Comments", request.feedback());
        data.put("CreatedDate", Instant.now().toString());
        data.put("Type", request.feedbackType().name);

        try {
            var result = docRef.set(data);
            var writtenResponse = result.get();
            LOG.info("Write result updated time : " + writtenResponse.getUpdateTime());
        } catch (InterruptedException | ExecutionException e) {
            LOG.severe(e.getMessage());
            return Result.of(new Exception(e));
        }
        return Result.of(new Response&amp;lt;&amp;gt;(request, MetaResponse.Of(httpRequest)));
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🧠 This is where the real magic happens. We take the incoming request, transform it into a Firestore document, and save it under the "firestore/feedback" path. Logging ensures we know when the document was successfully written.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Pro tip: You can dynamically generate document IDs with .document() or let Firestore auto-generate them using .add().&lt;/p&gt;

&lt;p&gt;🎭 &lt;br&gt;
At this point, Firestore and Micronaut are basically best friends—trading JSON over a coffee and laughing at how easy it was to build this.&lt;br&gt;
If bugs show up now, they’re probably just jealous they didn’t get invited to the architecture meeting.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;✅ Conclusion: Build Smart, Build Serverless&lt;br&gt;
Firestore proves to be an excellent choice when building scalable, event-driven, and AI-powered microservices in a serverless environment. With its seamless integration with Micronaut, automatic scaling, and native support for real-time data and AI tooling, you can focus on delivering business value instead of wrestling with infrastructure.&lt;/p&gt;

&lt;p&gt;So the next time someone asks, “SQL or NoSQL?” — you can confidently say:&lt;/p&gt;

&lt;p&gt;“It depends... but if you're building serverless and smart, Firestore’s got your back.” 🚀👨‍💻👩‍💻✨&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>java</category>
      <category>micronaut</category>
    </item>
    <item>
      <title>Data Oriented vs Object Oriented Programming in Java</title>
      <dc:creator>anand jaisy</dc:creator>
      <pubDate>Sun, 01 Jun 2025 08:20:09 +0000</pubDate>
      <link>https://forem.com/anand_jaisy_2f7644a12001b/data-oriented-vs-object-oriented-programming-in-java-1fgn</link>
      <guid>https://forem.com/anand_jaisy_2f7644a12001b/data-oriented-vs-object-oriented-programming-in-java-1fgn</guid>
      <description>&lt;h2&gt;
  
  
  Oops
&lt;/h2&gt;

&lt;p&gt;Object-Oriented Programming (OOP) promotes modeling complex entities and processes through objects that encapsulate both state and behavior. It emphasizes principles like encapsulation—where an object's behavior controls access to its internal state—and polymorphism, which allows different types of entities to be handled through a shared interface or common set of operations. The specific ways these principles are implemented can differ across object-oriented languages.&lt;/p&gt;

&lt;p&gt;Although some developers are quick to proclaim that object-oriented programming is a failed approach, the reality is more nuanced. Like any tool, OOP excels in certain contexts and falls short in others. Poorly applied OOP can lead to frustrating and overly complex designs, and many have encountered exaggerated or misguided implementations of its principles. However, by understanding where OOP shines and where it doesn't, we can make informed decisions—leveraging it when it adds value and opting for alternative approaches when it doesn't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Modeling a Factory Method Using Switch Expressions, Interfaces, and Classes with oops&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;we'll explore a simple and effective way to implement the Factory Method design pattern in Java using language features such as switch expressions, interfaces, and class-based polymorphism.&lt;/p&gt;

&lt;p&gt;We’ll start by defining an enum to represent different types of email events:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public enum EmailType {
    VERIFICATION,
    PASSWORD_RECOVERY,
    TENANT_WELCOME
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public interface IEmailEventService {
    void send(String messageBrokerQueue);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we define a common interface that all email event services will implement. This allows us to encapsulate behavior behind a unified contract—an essential aspect of object-oriented polymorphism:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class EmailEventService implements IEmailEventService{

    @Override
    public void send(String messageBrokerQueue) {

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

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class PasswordRecoveryEventService implements IEmailEventService{

    @Override
    public void send(String messageBrokerQueue) {

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

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class TenantMessagingService implements IEmailEventService{

    @Override
    public void send(String messageBrokerQueue) {

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

&lt;/div&gt;



&lt;p&gt;Lets build a factory method that uses a switch expression on the EmailType enum to dynamically instantiate the appropriate service class.&lt;/p&gt;

&lt;p&gt;By combining encapsulation, polymorphism, we can build clean and maintainable code that adheres to solid design principles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class EmailEventFactory {
    private final IEmailEventService verifyEmailEventService;
    private final IEmailEventService passwordRecoveryEmailEventService;
    private final IEmailEventService tenantWelcomeEmailEventService;

    public EmailEventFactory() {
        this.verifyEmailEventService = new EmailEventService();
        this.passwordRecoveryEmailEventService = new PasswordRecoveryEventService();
        this.tenantWelcomeEmailEventService = new TenantMessagingService();
    }
    public void getEmailEventService(EmailType type) {
        switch (type){
            case VERIFICATION: this.verifyEmailEventService.send("verify");
            case PASSWORD_RECOVERY:this.passwordRecoveryEmailEventService.send("passwordRecovery");
            case TENANT_WELCOME: this.tenantWelcomeEmailEventService.send("tenantWelcome");
                break;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first glance, this implementation appears clean and effective—and in many contexts, it is. However, it comes with some limitations that can lead to runtime issues if not carefully handled.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Potential Issues with This Approach&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lack of Exhaustiveness Checking&lt;/strong&gt;&lt;br&gt;
If you add a new constant to the EmailType enum, the compiler won’t warn you that your switch expression is missing a corresponding case. This can silently introduce bugs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Risk of Runtime Exceptions&lt;/strong&gt;&lt;br&gt;
If an unhandled EmailType is passed to the method, it will simply be ignored, or worse—lead to unexpected behavior. To mitigate this, it's common to add a default case:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;switch (type) {
    case VERIFICATION -&amp;gt; verifyEmailEventService.send("verify");
    case PASSWORD_RECOVERY -&amp;gt; passwordRecoveryEmailEventService.send("passwordRecovery");
    case TENANT_WELCOME -&amp;gt; tenantWelcomeEmailEventService.send("tenantWelcome");
    default -&amp;gt; throw new IllegalArgumentException("Unsupported email type: " + type);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No Compile-Time Guarantee&lt;/strong&gt;
Even with a default case, the compiler still cannot ensure all enum values are covered. This shifts the responsibility to the developer to handle all possible cases correctly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note -&lt;/strong&gt; &lt;code&gt;While modeling a factory using switch expressions is straightforward and often sufficient for small-scale applications, it has limitations in scalability and safety.&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Data-oriented
&lt;/h2&gt;

&lt;p&gt;Data-oriented programming promotes modeling information as immutable data and separating the business logic that operates on it. As the shift toward simpler, more focused programs has gained momentum, Java has introduced new features to better support this style—such as records for straightforward data modeling, sealed classes for representing fixed sets of alternatives, and pattern matching for flexible and expressive handling of polymorphic data.&lt;/p&gt;

&lt;p&gt;Records, sealed classes, and pattern matching are complementary features in Java that collectively enhance support for data-oriented programming.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Records provide a concise way to model immutable data structures.&lt;/li&gt;
&lt;li&gt;Sealed classes enable precise modeling of constrained hierarchies, allowing developers to define a fixed set of subclasses.&lt;/li&gt;
&lt;li&gt;Pattern matching offers a type-safe and expressive mechanism for working with polymorphic data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Java has introduced pattern matching in progressive stages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Initially, only type-test patterns were supported in instanceof checks.&lt;/li&gt;
&lt;li&gt;Subsequent enhancements extended support to switch statements, enabling more expressive control flow.&lt;/li&gt;
&lt;li&gt;Most recently, deconstruction patterns for records were introduced in Java 19, allowing developers to extract and operate on the internal components of records in a streamlined, declarative manner.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These features together promote cleaner, more maintainable code by aligning data representation closely with the logic that operates on it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Refactoring with a Data-Oriented Approach&lt;/strong&gt;&lt;br&gt;
Let’s now revisit the factory method from an earlier example and refactor it using data-oriented principles. By leveraging records, sealed interfaces, and switch expressions, we achieve improved readability, stronger compile-time safety, and better separation of concerns.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Component
public record EmailEventFactory(VerifyEmailEventService verifyEmailEventService,
                                PasswordRecoveryEventService passwordRecoveryEventService,
                                TenantMessagingService tenantMessagingService
                                ) {
    public IEmailEventService getEmailEventService(EmailType type) {
        return switch (type) {
            case VERIFICATION , TENANT_EMAIL_VERIFICATION-&amp;gt; verifyEmailEventService;
            case PASSWORD_RECOVERY -&amp;gt; passwordRecoveryEventService;
            case TENANT_WELCOME -&amp;gt; tenantMessagingService;
        };
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public sealed interface IEmailEventService permits EmailEventService, PasswordRecoveryEventService, TenantMessagingService, UserMessagingService {
     void send(String messageBrokerQueue);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The sealed interface ensures that all possible implementations are known and controlled, enhancing safety and discoverability.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Service
public record EmailEventService() implements IEmailEventService {
    @Override
    public void send(String messageBrokerQueue) {
     // Do the things   
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each service is implemented as a record, aligning with the principles of immutability and clear separation of concerns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Service
public record PasswordRecoveryEventService() implements IEmailEventService {
    @Override
    public void send(String messageBrokerQueue) {
            // Do the things 
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Service
public record UserMessagingService() implements IEmailEventService {
    @Override
    public void send(String messageBrokerQueue) {
    // Do the things 
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Benefits of This Refactoring&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compile-time safety: Thanks to sealed interfaces and exhaustive switch expressions.&lt;/li&gt;
&lt;li&gt;Immutability: Record types make the data model inherently immutable.&lt;/li&gt;
&lt;li&gt;Clean separation of logic: Each service handles a distinct responsibility, adhering to the Single Responsibility Principle.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br&gt;
The integration of records, sealed types, and pattern matching simplifies adherence to data-oriented principles, resulting in code that is more concise, readable, and reliable. While treating data as data may feel unfamiliar within Java’s object-oriented foundation, these modern features offer powerful tools that are well worth incorporating into our development practices.&lt;/p&gt;

</description>
      <category>java</category>
      <category>web</category>
      <category>data</category>
      <category>designpatterns</category>
    </item>
    <item>
      <title>Angular 19 and Spring authorization server as a single application</title>
      <dc:creator>anand jaisy</dc:creator>
      <pubDate>Mon, 31 Mar 2025 22:38:25 +0000</pubDate>
      <link>https://forem.com/anand_jaisy_2f7644a12001b/angular-19-and-spring-authorization-server-as-a-single-application-46mp</link>
      <guid>https://forem.com/anand_jaisy_2f7644a12001b/angular-19-and-spring-authorization-server-as-a-single-application-46mp</guid>
      <description>&lt;p&gt;Welcome, fellow developers! 🚀 If you're looking to integrate Spring Boot with Spring Security and set up an OAuth2 Authorization Server, you're in the right place. In this guide, we'll walk through the process step by step, ensuring a smooth and secure implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Setting Up the Spring Boot Project
&lt;/h2&gt;

&lt;p&gt;First, let’s create a new Spring Boot project with the necessary dependencies. Head over to Spring Initializr and generate a project with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spring Web (for REST APIs)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plugins {
  java
  id("org.springframework.boot") version "3.4.4"
  id("io.spring.dependency-management") version "1.1.7"
}

group = "spring.angular"
version = "0.0.1-SNAPSHOT"

java {
  toolchain {
    languageVersion = JavaLanguageVersion.of(21)
  }
}

repositories {
  mavenCentral()
}

dependencies {
  implementation("org.springframework.boot:spring-boot-starter-web")
  testImplementation("org.springframework.boot:spring-boot-starter-test")
  testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

tasks.withType&amp;lt;Test&amp;gt; {
  useJUnitPlatform()
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once generated, open the project in your favorite IDE (IntelliJ, Eclipse, or VS Code) and run the app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verifying the Spring Boot Application Startup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v3.4.4)

2025-03-31T08:37:22.524+11:00  INFO 31772 --- [SpringAngularApp] [           main] s.a.S.SpringAngularAppApplication        : No active profile set, falling back to 1 default profile: "default"
2025-03-31T08:37:22.878+11:00  INFO 31772 --- [SpringAngularApp] [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (http)
2025-03-31T08:37:23.054+11:00  INFO 31772 --- [SpringAngularApp] [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (http) with context path '/'

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

&lt;/div&gt;



&lt;p&gt;As from the logs we can see that our app is running on port &lt;strong&gt;8080&lt;/strong&gt;. Navigate to the browser and hit enter on the url &lt;a href="http://localhost:8080/" rel="noopener noreferrer"&gt;http://localhost:8080/&lt;/a&gt; we should the screen something like below, which mean our spring boot application is running fine. If everything is configured correctly, you should see a landing page as below&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%2Flij79hr4gg86l8l101zt.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%2Flij79hr4gg86l8l101zt.png" alt="Spring boot application" width="800" height="230"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Its time to add Angular app
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Install the Angular CLI
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g @angular/cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create a workspace and initial application
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng new ui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can select select any choice, however we will stick to SCSS.&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%2Fnpxfme4xn1m97pmyrqoe.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%2Fnpxfme4xn1m97pmyrqoe.png" alt="css choice" width="800" height="172"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We don't want SSR, our choice will be N&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%2Fa3r9u690ztc69n0zkpjh.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%2Fa3r9u690ztc69n0zkpjh.png" alt="Server side rendering" width="800" height="72"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The angular CLI will install few dependecies&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%2F5oxk264jjj3amfo3i469.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%2F5oxk264jjj3amfo3i469.png" alt="Dependency" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Onces the dependecies are install, we can see the below project structure&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%2Fojcqoyf6qs2lht26d4nf.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%2Fojcqoyf6qs2lht26d4nf.png" alt="Project structure" width="800" height="1077"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd new ui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Run the application
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng serve --open
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enter the URL &lt;a href="http://localhost:4200/" rel="noopener noreferrer"&gt;http://localhost:4200/&lt;/a&gt; in the browser and we will the angular app is running &lt;/p&gt;

&lt;h2&gt;
  
  
  Development Workflow
&lt;/h2&gt;

&lt;p&gt;During development, it's perfectly fine (and often preferable) to run Angular and Spring Boot as separate applications:&lt;/p&gt;

&lt;p&gt;Angular CLI: Serves the frontend on &lt;a href="http://localhost:4200" rel="noopener noreferrer"&gt;http://localhost:4200&lt;/a&gt; with hot-reload for fast development.&lt;/p&gt;

&lt;p&gt;Spring Boot: Runs the backend API on &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This separation allows for:&lt;br&gt;
✔ Faster frontend iterations (thanks to Angular's live reload)&lt;br&gt;
✔ Independent debugging of backend APIs&lt;br&gt;
✔ Mock API responses during early development&lt;/p&gt;
&lt;h2&gt;
  
  
  The Production Challenge
&lt;/h2&gt;

&lt;p&gt;In production, we typically want to serve both applications as a single unit for:&lt;br&gt;
✔ Simplified deployment&lt;br&gt;
✔ Reduced cross-origin issues (CORS)&lt;br&gt;
✔ Better performance (serving static assets directly from the backend) &lt;/p&gt;
&lt;h2&gt;
  
  
  Configuring Angular to Deploy as Spring Boot Static Content for production
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Understanding the Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By default, Angular builds to /dist/ui, but we need it to output directly to Spring Boot's static resources folder where it can be automatically served. Here's how to make this work seamlessly:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Modify Angular's Output Path&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"architect": {
  "build": {
    "builder": "@angular-devkit/build-angular:application",
    "options": {
      "outputPath": "dist/ui",
    },
}
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets change the &lt;code&gt;"outputPath": "dist/ui"&lt;/code&gt; to the spring boot resource directory&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"architect": {
  "build": {
    "builder": "@angular-devkit/build-angular:application",
    "options": {
      "outputPath": "../src/main/resources/static",  // Changed from "dist/ui"
      "index": "src/index.html",
      // ... rest of your config
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;"outputPath": "../src/main/resources/static"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Since now the &lt;strong&gt;outputPath&lt;/strong&gt; is pointing to the spring boot resources, we can run angular CLI command to build the files, we must point to the root dictory of the angular app in our case &lt;strong&gt;ui&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Build and Verify&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng build --configuration production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will see the below logs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ ng build --configuration production
Initial chunk files   | Names         |  Raw size | Estimated transfer size
main-3BERZHFR.js      | main          | 208.01 kB |                56.74 kB
polyfills-FFHMD2TL.js | polyfills     |  34.52 kB |                11.28 kB
styles-5INURTSO.css   | styles        |   0 bytes |                 0 bytes

                      | Initial total | 242.53 kB |                68.02 kB

Application bundle generation complete. [2.279 seconds]

Output location: /Users/san/project/Sample/AngularTest/src/main/resources/static
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the output location here &lt;code&gt;Output location: /Users/san/project/Sample/AngularTest/src/main/resources/static&lt;/code&gt;. The files are been output the the spring boot resources directory. &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%2Fxc5hpa2u1mfcyqaf3kxf.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%2Fxc5hpa2u1mfcyqaf3kxf.png" alt="Files location" width="800" height="524"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Run the spring boot app, and head over to the browser &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt; we will see there is no angular app.&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%2F1vfnl49iow6ujbnguvru.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%2F1vfnl49iow6ujbnguvru.png" alt="Angular app + spring boot" width="800" height="354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have looked closely to the &lt;code&gt;resources &amp;gt;&amp;gt; static&lt;/code&gt; directory, there is browser directory is created by angular build command, and if we look into that directory we can see the index.html and .js file. For angular to work that needs to be on outside of the browser directory, because spring doesn't know anything about browser directory. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution 1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We can move those files outside of the browser as below&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%2Ffsjg4hzj0sul2lzb7niz.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%2Ffsjg4hzj0sul2lzb7niz.png" alt="files outside of browser" width="800" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, if we run our spring boot app, and navigate to &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt; we will see angular application&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%2Faxipnlhqicqrg6g2pgu4.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%2Faxipnlhqicqrg6g2pgu4.png" alt="Angular app running" width="800" height="212"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Well this works right, hmmmm not that effective&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The problem with this solution is that we need to manually move the files, lets see how we can fix this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Better Solution&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            "outputPath": {
              "base": "../src/main/resources/static",
              "browser": ""
            },
            "deleteOutputPath": false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Include &lt;code&gt;browser =""&lt;/code&gt; to generate file inside the static directory and don't delete other files by including &lt;code&gt;"deleteOutputPath": false&lt;/code&gt;,. Now if we run the command &lt;code&gt;ng build --configuration production&lt;/code&gt; we can see all the files are generated within static&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add a new component&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng g c Home // This will generate new Home Component
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Include this component in the router section&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Routes } from '@angular/router';
import {HomeComponent} from './home/home.component';

export const routes: Routes = [{ path: 'home', component: HomeComponent },];

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

&lt;/div&gt;



&lt;p&gt;For local development we can reply on &lt;code&gt;ng server&lt;/code&gt;. The routing will work on &lt;a href="http://localhost:4200/home" rel="noopener noreferrer"&gt;http://localhost:4200/home&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;For spring boot to include the router we need to build again&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng build --configuration production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we navigate to &lt;code&gt;http://localhost:8080/home&lt;/code&gt; we will face an issue as below&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%2Flij79hr4gg86l8l101zt.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%2Flij79hr4gg86l8l101zt.png" alt="Spring boot application" width="800" height="230"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To fix this we have to do configuration for &lt;code&gt;WebMvcConfigurer&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/static/")
                .resourceChain(true)
                .addResolver(new PathResourceResolver() {
                    @Override
                    protected Resource getResource(String resourcePath, Resource location) throws IOException {
                        Resource requestedResource = location.createRelative(resourcePath);
                        return requestedResource.exists() &amp;amp;&amp;amp; requestedResource.isReadable()
                                ? requestedResource
                                : new ClassPathResource("/static/index.html");
                    }
                });
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Development vs Production Workflow
&lt;/h2&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%2Fws0uqvuz5ubk90wn9rf7.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%2Fws0uqvuz5ubk90wn9rf7.png" alt="Development vs production" width="800" height="178"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now if we run the spring boot app and navigate to &lt;code&gt;http://localhost:8080/home&lt;/code&gt; we will see our home component.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For development, configure Angular's proxy to avoid CORS issues:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/proxy.conf.json
{
  "/api": {
    "target": "http://localhost:8080",
    "secure": false
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Spring authorization server
&lt;/h2&gt;

&lt;p&gt;Update the dependency to include spring authorization server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependencies {
    implementation("org.springframework.boot:spring-boot-starter-oauth2-authorization-server")
    implementation("org.springframework.boot:spring-boot-starter-validation")
    implementation("org.springframework.boot:spring-boot-starter-webflux")
    developmentOnly("org.springframework.boot:spring-boot-devtools")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("io.projectreactor:reactor-test")
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if we try to access &lt;code&gt;http://localhost:8080/&lt;/code&gt; the basic login page of spring security displayed.&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%2F5zxjbt3eydyo61y1u6u2.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%2F5zxjbt3eydyo61y1u6u2.png" alt="login" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We need to configure the basic setup for spring authorization server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    @Order(1)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
            throws Exception {
        OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
                OAuth2AuthorizationServerConfigurer.authorizationServer();

        http
                .securityMatcher(authorizationServerConfigurer.getEndpointsMatcher())
                .with(authorizationServerConfigurer, (authorizationServer) -&amp;gt;
                        authorizationServer
                                .oidc(Customizer.withDefaults())    // Enable OpenID Connect 1.0
                )
                .authorizeHttpRequests((authorize) -&amp;gt;
                        authorize
                                .anyRequest().authenticated()
                )
                // Redirect to the login page when not authenticated from the
                // authorization endpoint
                .exceptionHandling((exceptions) -&amp;gt; exceptions
                        .defaultAuthenticationEntryPointFor(
                                new LoginUrlAuthenticationEntryPoint("/login"),
                                new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
                        )
                );

        return http.build();
    }

    @Bean
    @Order(2)
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
            throws Exception {
        http
                .authorizeHttpRequests((authorize) -&amp;gt; authorize
                        .anyRequest().authenticated()
                )
                // Form login handles the redirect to the login page from the
                // authorization server filter chain
                .formLogin(Customizer.withDefaults());

        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails userDetails = User.withDefaultPasswordEncoder()
                .username("user")
                .password("password")
                .roles("USER")
                .build();

        return new InMemoryUserDetailsManager(userDetails);
    }

    @Bean
    public RegisteredClientRepository registeredClientRepository() {
        RegisteredClient oidcClient = RegisteredClient.withId(UUID.randomUUID().toString())
                .clientId("oidc-client")
                .clientSecret("{noop}secret")
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
                .redirectUri("http://127.0.0.1:8080/login/oauth2/code/oidc-client")
                .postLogoutRedirectUri("http://127.0.0.1:8080/")
                .scope(OidcScopes.OPENID)
                .scope(OidcScopes.PROFILE)
                .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
                .build();

        return new InMemoryRegisteredClientRepository(oidcClient);
    }

    @Bean
    public JWKSource&amp;lt;SecurityContext&amp;gt; jwkSource() {
        KeyPair keyPair = generateRsaKey();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        RSAKey rsaKey = new RSAKey.Builder(publicKey)
                .privateKey(privateKey)
                .keyID(UUID.randomUUID().toString())
                .build();
        JWKSet jwkSet = new JWKSet(rsaKey);
        return new ImmutableJWKSet&amp;lt;&amp;gt;(jwkSet);
    }

    private static KeyPair generateRsaKey() {
        KeyPair keyPair;
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(2048);
            keyPair = keyPairGenerator.generateKeyPair();
        }
        catch (Exception ex) {
            throw new IllegalStateException(ex);
        }
        return keyPair;
    }

    @Bean
    public JwtDecoder jwtDecoder(JWKSource&amp;lt;SecurityContext&amp;gt; jwkSource) {
        return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
    }

    @Bean
    public AuthorizationServerSettings authorizationServerSettings() {
        return AuthorizationServerSettings.builder().build();
    }

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

&lt;/div&gt;



&lt;p&gt;Since the basic setup is using in-memory user. So using with those credential if we use login the angular app works.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;The Best of Both Worlds&lt;/strong&gt;&lt;br&gt;
By configuring Angular to build directly into Spring Boot's static resources folder, we've created a powerful full-stack solution that:&lt;/p&gt;

&lt;p&gt;✔ Simplifies Deployment - A single Spring Boot JAR contains both frontend and backend&lt;br&gt;
✔ Improves Performance - Static assets are served efficiently by the embedded Tomcat server&lt;br&gt;
✔ Maintains Flexibility - Keep separate dev servers during development while unifying for production&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>angular</category>
      <category>springsecurity</category>
      <category>java</category>
    </item>
    <item>
      <title>Tailwind v4 + Java Template Engine and spring boot</title>
      <dc:creator>anand jaisy</dc:creator>
      <pubDate>Thu, 20 Mar 2025 09:21:17 +0000</pubDate>
      <link>https://forem.com/anand_jaisy_2f7644a12001b/install-tailwind-css-4-java-template-engine-spring-boot-2d48</link>
      <guid>https://forem.com/anand_jaisy_2f7644a12001b/install-tailwind-css-4-java-template-engine-spring-boot-2d48</guid>
      <description>&lt;p&gt;As a Spring Boot developer, you’ve likely faced the challenge of adding sleek, modern styling to your web application. Enter Tailwind CSS—a utility-first CSS framework that has taken the development world by storm. Known for its flexibility and efficiency, Tailwind CSS allows you to build custom designs directly in your markup, without ever leaving your HTML. But how do you seamlessly integrate Tailwind CSS into your Spring Boot project while keeping your production build lean and optimized? In this article, we’ll walk you through the steps to set up Tailwind CSS in your Spring Boot application, ensuring that only the styles you actually use make it into your final build. Let’s dive in!&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started &amp;amp; Prerequisites
&lt;/h2&gt;

&lt;p&gt;You will need to have Node and NPM installed. To verify that they are installed you can run the following commands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node -v # v20.2.0
npm -v # 9.8.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setting Up a Spring Boot Project with Tailwind CSS
&lt;/h2&gt;

&lt;p&gt;Create a new Spring Boot project using the Spring Initializr. We'll choose the following options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Project: &lt;code&gt;Gradle Project&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Language: &lt;code&gt;Java&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Spring Boot: &lt;code&gt;Latest version&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Dependencies: &lt;code&gt;Web, Java Template Engine, and Spring Boot DevTools&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once we have our project set up, we'll need to create a new folder called &lt;code&gt;node&lt;/code&gt; in the &lt;code&gt;src/main&lt;/code&gt; directory. This is where we'll be doing our Tailwind CSS setup and development.&lt;/p&gt;

&lt;p&gt;Inside the &lt;code&gt;node&lt;/code&gt; folder, we'll need to initialize a new npm project by running the following command in the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;package.json&lt;/code&gt; file should get created as below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "name": "node",
  "version": "1.0.0",
  "description": "Spring Authorization server UI ",
  "main": "index.js",
  "scripts": {},
  "author": "SanJaisy",
  "license": "ISC",
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Installing and Configuring Tailwind CSS CLI in a Node Project
&lt;/h2&gt;

&lt;p&gt;To begin, we need to install the Tailwind CSS CLI in the &lt;code&gt;node&lt;/code&gt; directory. Run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install tailwindcss @tailwindcss/cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dependencies should get updated as below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  "dependencies": {
    "@tailwindcss/cli": "^4.0.7",
    "tailwindcss": "^4.0.7"
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuring the Build Script
&lt;/h2&gt;

&lt;p&gt;Once the dependencies are installed, add the following scripts to package.json to generate a minified CSS file from all the JTE templates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "name": "node",
  "version": "1.0.0",
  "description": "Spring Authorization server UI ",
  "main": "index.js",
  "scripts": {
    "build": "npx @tailwindcss/cli -i ./style.css -o ../resources/static/main.css --minify",
    "watch": "npx @tailwindcss/cli --watch -i ./style.css -o ../resources/static/main.css --minify"
  },
  "author": "SanJaisy",
  "license": "ISC",
  "dependencies": {
    "@tailwindcss/cli": "^4.0.14",
    "tailwindcss": "^4.0.14"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we now run the &lt;code&gt;npm run build&lt;/code&gt; command we can see the main.css file is created with all the JTE css.&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%2Fmvv5booucr0w1cpz8zko.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%2Fmvv5booucr0w1cpz8zko.png" alt="Sample Project" width="686" height="766"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With version 4 we don't need tailwind.config file, just we need to make sure we include below in our style files&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@import "tailwindcss";
@source "../jte/**";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;@source "../jte/**";&lt;/code&gt; this is the directory where all my JTE templates are present.&lt;/p&gt;

&lt;p&gt;Make sure to include &lt;code&gt;main.css&lt;/code&gt; file in the &lt;code&gt;index.jte&lt;/code&gt;&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;!DOCTYPE html&amp;gt;
&amp;lt;html lang="en"&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset="UTF-8"&amp;gt;
    &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&amp;gt;
    &amp;lt;link rel="stylesheet" href="./main.css"&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body class="bg-gray-100"&amp;gt;
${content}
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>springboot</category>
      <category>tailwindcss</category>
      <category>javatemplateengine</category>
      <category>java</category>
    </item>
    <item>
      <title>Spring Authorization server + spring security with custom user details service for flexible data-driven authentication</title>
      <dc:creator>anand jaisy</dc:creator>
      <pubDate>Thu, 23 Jan 2025 01:36:11 +0000</pubDate>
      <link>https://forem.com/anand_jaisy_2f7644a12001b/spring-authorization-server-spring-security-with-custom-user-details-service-for-flexible-105b</link>
      <guid>https://forem.com/anand_jaisy_2f7644a12001b/spring-authorization-server-spring-security-with-custom-user-details-service-for-flexible-105b</guid>
      <description>&lt;h2&gt;
  
  
  Spring Authorization Server
&lt;/h2&gt;

&lt;p&gt;The Spring Authorization Server is a framework designed to implement the OAuth 2.1 and OpenID Connect 1.0 specifications, along with other related standards. Built on Spring Security, it offers a secure, lightweight, and customizable foundation for creating Identity Providers compliant with OpenID Connect 1.0 and OAuth2 Authorization Server solutions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.spring.io/spring-authorization-server/reference/overview.html#feature-list" rel="noopener noreferrer"&gt;Feature List&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Spring Security and how does it work?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;short answer&lt;/strong&gt;&lt;br&gt;
Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.&lt;/p&gt;

&lt;p&gt;At its heart, Spring Security is essentially a collection of servlet filters designed to enhance your web application with robust authentication and authorization features.&lt;/p&gt;

&lt;p&gt;Spring Security also meshes well with frameworks like Spring Web MVC or Spring Boot, supporting standards such as OAuth2 and SAML. It automatically generates login and logout interfaces and safeguards your application from common security vulnerabilities like CSRF.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Well, that's not very helpful, is it?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's delve into web security to grasp the essentials of its security workflow.&lt;/p&gt;

&lt;p&gt;To become a Spring Security expert, you must first grasp these three core concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Authentication&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authorization&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Servlet Filters&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; - &lt;em&gt;Don't bypass this section; it lays the groundwork for all Spring Security functionalities.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Authentication
&lt;/h2&gt;

&lt;p&gt;You need to access your bank account online to check your balance or make a transaction. Typically this is done using &lt;code&gt;UserName&lt;/code&gt; and &lt;code&gt;Password&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User&lt;/strong&gt;: "I'm John Doe. My username is: johndoe1985."&lt;br&gt;
&lt;strong&gt;Bank's System&lt;/strong&gt;: "Please verify your identity. What's your password?"&lt;br&gt;
&lt;strong&gt;User&lt;/strong&gt;: "My password is: &lt;a href="mailto:secureB@nk2023"&gt;secureB@nk2023&lt;/a&gt;."&lt;br&gt;
&lt;strong&gt;Bank's System&lt;/strong&gt;: "Welcome, John Doe. Here's your account overview."&lt;/p&gt;
&lt;h2&gt;
  
  
  Authorization
&lt;/h2&gt;

&lt;p&gt;For basic applications, authentication alone might suffice: Once a user logs in, they're granted access to all areas of the application.&lt;/p&gt;

&lt;p&gt;However, in most applications, there are permissions or roles in play. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User&lt;/strong&gt;: "Let me play with that transaction …​."&lt;br&gt;
&lt;strong&gt;Bank's System&lt;/strong&gt;: "One second, I need to check your permissions first…​..yes Mr. John Doe, you have the right clearance level. Enjoy."&lt;br&gt;
&lt;strong&gt;User&lt;/strong&gt;: "I'll transfer &lt;code&gt;1M&lt;/code&gt; ha ha ha …​ &lt;code&gt;Kidding kidding&lt;/code&gt;"&lt;/p&gt;
&lt;h2&gt;
  
  
  Servlet Filters
&lt;/h2&gt;

&lt;p&gt;Now, let's explore Servlet Filters. How do they relate to authentication and authorization?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why use Servlet Filters?&lt;/strong&gt;&lt;br&gt;
Every Spring web application revolves around a single servlet: the trusty DispatcherServlet. Its primary role is to route incoming HTTP requests (such as those from a browser) to the appropriate @Controller or @RestController for handling.&lt;/p&gt;

&lt;p&gt;Here’s the deal: the DispatcherServlet itself doesn’t have any built-in security features, and you probably don’t want to handle raw HTTP Basic Auth headers directly in your &lt;code&gt;@Controllers&lt;/code&gt;. Ideally, authentication and authorization should be taken care of before a request even reaches your &lt;code&gt;@Controllers&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Fortunately, in the Java web environment, you can achieve this by placing filters before servlets. This means you could consider creating a SecurityFilter and setting it up in your Tomcat (servlet container/application server) to intercept and process every incoming HTTP request before it reaches your servlet.&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%2F9vr0fmpk6b25jqxq9u6l.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%2F9vr0fmpk6b25jqxq9u6l.png" alt="Security context" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A SecurityFilter has roughly 4 tasks &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First, the filter needs to extract a username/password from the request. It could be via a Basic Auth HTTP Header, or form fields, or a cookie, etc.&lt;/li&gt;
&lt;li&gt;Then the filter needs to validate that username/password combination against something, like a database. &lt;/li&gt;
&lt;li&gt;The filter needs to check, after successful authentication, that the user is authorized to access the requested URI. &lt;/li&gt;
&lt;li&gt;If the request survives all these checks, then the filter can l
et the request go through to your DispatcherServlet, i.e. your @Controllers.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  FilterChains
&lt;/h2&gt;

&lt;p&gt;In practice, we'd break a single filter down into several, which you would then link together.&lt;/p&gt;

&lt;p&gt;Here's how an incoming HTTP request would travel:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First, it passes through a &lt;code&gt;LoginMethodFilter&lt;/code&gt;...&lt;/li&gt;
&lt;li&gt;Next, it goes through an &lt;code&gt;AuthenticationFilter&lt;/code&gt;...&lt;/li&gt;
&lt;li&gt;Then, it moves to an &lt;code&gt;AuthorizationFilter&lt;/code&gt;...&lt;/li&gt;
&lt;li&gt;And finally, it reaches your servlet.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This setup is known as a FilterChain.&lt;/p&gt;

&lt;p&gt;By using a filter (or a chain of filters), you can effectively manage all authentication and authorization challenges in your application without altering the core implementation of your &lt;code&gt;@RestControllers&lt;/code&gt; or &lt;code&gt;@Controllers&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Spring’s DefaultSecurityFilterChain
&lt;/h2&gt;

&lt;p&gt;Imagine you’ve configured Spring Security properly and started your web application. You’ll notice a log message that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2020-02-25 10:24:27.875  INFO 11116 --- [           main] o.s.s.web.DefaultSecurityFilterChain     : Creating filter chain: any request, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@46320c9a, org.springframework.security.web.context.SecurityContextPersistenceFilter@4d98e41b, org.springframework.security.web.header.HeaderWriterFilter@52bd9a27, org.springframework.security.web.csrf.CsrfFilter@51c65a43, org.springframework.security.web.authentication.logout.LogoutFilter@124d26ba, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@61e86192, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@10980560, org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@32256e68, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@52d0f583, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@5696c927, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@5f025000, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@5e7abaf7, org.springframework.security.web.session.SessionManagementFilter@681c0ae6, org.springframework.security.web.access.ExceptionTranslationFilter@15639d09, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@4f7be6c8]|
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expanding that single line reveals that Spring Security doesn’t just add one filter—it sets up an entire filter chain with 15 (!) different filters.&lt;/p&gt;

&lt;p&gt;When an HTTP request arrives, it passes through each of these 15 filters in sequence before finally reaching your @RestControllers. The order of these filters is crucial, as the request is processed from the top of the chain to the bottom.&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%2F8cxi525m2fjfgvgw2h32.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%2F8cxi525m2fjfgvgw2h32.png" alt="security chain" width="800" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Analyzing Spring’s FilterChain
&lt;/h2&gt;

&lt;p&gt;Diving into the details of every filter in the chain would take us too far, but here are explanations for a few key filters. For a deeper understanding of the others, you can explore &lt;a href="https://github.com/spring-projects/spring-security" rel="noopener noreferrer"&gt;Spring Security’s source code&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;BasicAuthenticationFilter&lt;/strong&gt;: Tries to find a Basic Auth HTTP Header on the request and if found, tries to authenticate the user with the header’s username and password.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UsernamePasswordAuthenticationFilter&lt;/strong&gt;: Tries to find a username/password request parameter/POST body and if found, tries to authenticate the user with those values. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DefaultLoginPageGeneratingFilter&lt;/strong&gt;: Generates a login page for you, if you don’t explicitly disable that feature. THIS filter is why you get a default login page when enabling Spring Security. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DefaultLogoutPageGeneratingFilter&lt;/strong&gt;: Generates a logout page for you, if you don’t explicitly disable that feature. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FilterSecurityInterceptor&lt;/strong&gt;: Does your authorization.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Joke
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Question&lt;/strong&gt; - &lt;em&gt;Why did the HTTP request break up with the Spring Security filter?&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;Answer&lt;/strong&gt; - &lt;em&gt;Because every time it tried to get closer, the filter said, "Hold on! Let me check you out first!"&lt;/em&gt; 😄&lt;/p&gt;

&lt;p&gt;Yeah break ........ Whoa, hold up... that was way too much security talk for one go!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Setup Spring Authorization server
&lt;/h2&gt;

&lt;p&gt;The easiest way to begin using Spring Authorization Server is by creating a Spring Boot-based application. You can use &lt;a href="https://start.spring.io/" rel="noopener noreferrer"&gt;start.spring.io&lt;/a&gt; to generate a basic project.&lt;/p&gt;

&lt;p&gt;The only dependency required is &lt;code&gt;implementation("org.springframework.boot:spring-boot-starter-oauth2-authorization-server")&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We will add two more to do more action&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependencies {
    implementation("org.springframework.boot:spring-boot-starter-oauth2-authorization-server")
    implementation("org.springframework.boot:spring-boot-starter-webflux")
    implementation("org.springframework.boot:spring-boot-starter-validation")
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;How to configure Spring Security&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With the latest Spring Security and/or Spring Boot versions, the way to configure Spring Security is by having a class that: Is annotated with &lt;code&gt;@EnableWebSecurity&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Configuration
@EnableWebSecurity
public class SecurityConfig {
    private static final String[] ALLOW_LIST = {"/oauth2/token", "/userinfo"};
    //This is primarily configured to handle OAuth2 and OpenID Connect specific endpoints. It sets up the security for the authorization server, handling token endpoints, client authentication, etc.
    @Bean (1)
    @Order(1)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
        OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = OAuth2AuthorizationServerConfigurer.authorizationServer();
        http
                .cors(Customizer.withDefaults())
                .authorizeHttpRequests(authz -&amp;gt; authz
                        .requestMatchers(ALLOW_LIST).permitAll()
                        .requestMatchers("/**", "/oauth2/jwks/").hasAuthority("SCOPE_keys.write")
                        .anyRequest()
                        .authenticated())
                .securityMatchers(matchers -&amp;gt;
                        matchers.requestMatchers(antMatcher("/oauth2/**"), authorizationServerConfigurer.getEndpointsMatcher()))
                .with(authorizationServerConfigurer, (authorizationServer) -&amp;gt;
                        authorizationServer
                        .oidc(Customizer.withDefaults()))    // Enable OpenID Connect 1.0

                // Redirect to the login page when not authenticated from the
                // authorization endpoint
                .exceptionHandling((exceptions) -&amp;gt; exceptions
                        .defaultAuthenticationEntryPointFor(
                                new LoginUrlAuthenticationEntryPoint("/login"),
                                new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
                        ))
                // Accept access tokens for User Info and/or Client Registration
                .oauth2ResourceServer((oauth2) -&amp;gt; oauth2.jwt(Customizer.withDefaults()));
        return http.build();
    }

    // This configuration is set up for general application security, handling standard web security features like form login for paths not specifically managed by the OAuth2 configuration.
    @Bean (2)
    @Order(2)
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
            throws Exception {
        http
                .authorizeHttpRequests((authorize) -&amp;gt; authorize
                        .requestMatchers("/login", "/error", "/main.css")
                        .permitAll()
                        .anyRequest()
                        .authenticated()
                )
                // Form login handles the redirect to the login page from the
                // authorization server filter chain
                .formLogin((login) -&amp;gt; login.loginPage("/login"));

        return http.build();
    }

    @Bean (3)
    public JWKSource&amp;lt;SecurityContext&amp;gt; jwkSource() {
        KeyPair keyPair = generateRsaKey();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        RSAKey rsaKey = new RSAKey.Builder(publicKey)
                .privateKey(privateKey)
                .keyID(UUID.randomUUID().toString())
                .build();
        JWKSet jwkSet = new JWKSet(rsaKey);
        return new ImmutableJWKSet&amp;lt;&amp;gt;(jwkSet);
    }

    private static KeyPair generateRsaKey() {
        KeyPair keyPair;
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(2048);
            keyPair = keyPairGenerator.generateKeyPair();
        } catch (Exception ex) {
            throw new IllegalStateException(ex);
        }
        return keyPair;
    }


    @Bean (4)
    public JwtDecoder jwtDecoder(JWKSource&amp;lt;SecurityContext&amp;gt; jwkSource) {
        return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
    }

    @Bean (5)
    public AuthorizationServerSettings authorizationServerSettings() {
        return AuthorizationServerSettings
                .builder()
                .build();
    }

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

&lt;/div&gt;



&lt;p&gt;(1) : A Spring Security filter chain for the &lt;a href="https://docs.spring.io/spring-authorization-server/reference/protocol-endpoints.html" rel="noopener noreferrer"&gt;Protocol Endpoints&lt;/a&gt;. &lt;br&gt;
(2) : A Spring Security filter chain for &lt;a href="https://docs.spring.io/spring-security/reference/servlet/authentication/index.html" rel="noopener noreferrer"&gt;authentication&lt;/a&gt;.&lt;br&gt;
(3) : An instance of &lt;code&gt;com.nimbusds.jose.jwk.source.JWKSource&lt;/code&gt; for signing access tokens.&lt;br&gt;
(4) : An instance of &lt;code&gt;JwtDecoder&lt;/code&gt; for decoding signed access tokens.&lt;br&gt;
(5) : An instance of &lt;code&gt;AuthorizationServerSettings&lt;/code&gt; to configure Spring Authorization Server.&lt;/p&gt;

&lt;p&gt;Lets configure CORS to allow certain URLs to our application&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Configuration
public class CorsConfig {

    @Bean
    public UrlBasedCorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.addAllowedOrigin("http://localhost:3000/"); // Change to specific domains in production
        configuration.addAllowedMethod("*");
        configuration.addAllowedHeader("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;CorsConfiguration&lt;/strong&gt;&lt;br&gt;
This class is used to define the CORS rules. In this case:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;addAllowedOrigin("&lt;a href="http://localhost:3000/%22):" rel="noopener noreferrer"&gt;http://localhost:3000/"):&lt;/a&gt; Allows requests from &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;. This is useful for local development when your frontend is running on a different port. In production, replace this with your actual domains.&lt;/li&gt;
&lt;li&gt;addAllowedMethod("*"): Allows all HTTP methods (e.g., GET, POST, PUT, DELETE, etc.).&lt;/li&gt;
&lt;li&gt;addAllowedHeader("*"): Allows all HTTP headers in requests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;UrlBasedCorsConfigurationSource&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A class that maps URL patterns (like /**) to specific CORS configurations.&lt;/li&gt;
&lt;li&gt;registerCorsConfiguration("/&lt;strong&gt;", configuration): Applies the defined CORS rules (configuration) to all endpoints (/&lt;/strong&gt;) in the application.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Wow, that’s a lot of configuration! But that’s the magic of the Spring Framework—it handles all the heavy lifting behind the scenes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  It is time to configure the Clients
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Configuration
public class Clients {
    @Bean
    public RegisteredClientRepository registeredClientRepository() {
        RegisteredClient oidcClient = RegisteredClient.withId(UUID.randomUUID().toString())
                .clientId("stomble")
                .clientAuthenticationMethod(ClientAuthenticationMethod.NONE)
                .authorizationGrantTypes(types -&amp;gt; {
                    types.add(AuthorizationGrantType.AUTHORIZATION_CODE);
                    types.add(AuthorizationGrantType.REFRESH_TOKEN);
                })
                .redirectUris(redirectUri -&amp;gt; {
                    redirectUri.add("http://localhost:3000");
                    redirectUri.add("https://oauth.pstmn.io/v1/callback");
                    redirectUri.add("http://localhost:3000/signin-callback");
                })
                .postLogoutRedirectUri("http://localhost:3000")
                .scopes(score -&amp;gt; {
                    score.add(OidcScopes.OPENID);
                    score.add(OidcScopes.PROFILE);
                    score.add(OidcScopes.EMAIL);
                })
                .clientSettings(ClientSettings.builder()
                        .requireAuthorizationConsent(false)
                        .requireProofKey(true)
                        .build())
                .build();
        return new InMemoryRegisteredClientRepository(oidcClient);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Few things we have done above&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;clientId&lt;/strong&gt;: A unique Identifier to allow access&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;clientAuthenticationMethod&lt;/strong&gt; : Defining Authentication method&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;redirectUris&lt;/strong&gt; Allowing only the defined URLs &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;authorizationGrantTypes&lt;/strong&gt; &lt;code&gt;authorization_code&lt;/code&gt; &lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  UserDetailsService
&lt;/h2&gt;

&lt;p&gt;UserDetailsService is used by &lt;code&gt;DaoAuthenticationProvider&lt;/code&gt; for retrieving a &lt;strong&gt;username&lt;/strong&gt;, a &lt;strong&gt;password&lt;/strong&gt;, and other attributes for authenticating with a username and password. Spring Security provides &lt;code&gt;in-memory&lt;/code&gt;, &lt;code&gt;JDBC&lt;/code&gt;, and &lt;code&gt;caching&lt;/code&gt; implementations of UserDetailsService.&lt;/p&gt;

&lt;p&gt;You can define custom authentication by exposing a custom UserDetailsService as a bean.&lt;/p&gt;
&lt;h2&gt;
  
  
  InMemoryUserDetailsManager
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Configuration
public class UserConfig {

    @Bean
    public UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) {
        UserDetails userDetailFirst = User.builder()
                .username("user1")
                .password(passwordEncoder.encode("password"))
                .roles("USER")
                .build();
        UserDetails userDetailSecond = User.builder()
                .username("user2")
                .password(passwordEncoder.encode("password"))
                .roles("USER")
                .build();
        return new InMemoryUserDetailsManager(List.of(userDetailFirst, userDetailSecond));
    }
}

@Bean
public PasswordEncoder passwordEncoder() {
   return new BCryptPasswordEncoder();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Once we launch the application, our OIDC and OAuth2 setup with Spring Authorization Server should function correctly. However, you'll notice we've employed &lt;code&gt;InMemoryUserDetailsManager&lt;/code&gt;, which is fine for demos or prototyping. But for a production environment, it's not advisable because all data vanishes upon application restart.&lt;/p&gt;
&lt;h2&gt;
  
  
  JdbcUserDetailsManager in Spring Security
&lt;/h2&gt;

&lt;p&gt;JdbcUserDetailsManager is a feature within Spring Security that uses JDBC to handle user credentials and roles by connecting to a relational database. It's ideal when your application can work with the standard schema for user tables that Spring Security expects.&lt;/p&gt;

&lt;p&gt;The schema that is available from Spring security &lt;code&gt;org/springframework/security/core/userdetails/jdbc/users.ddl&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- Create users and authorities tables for JdbcUserDetailsManager
CREATE TABLE users (
    username VARCHAR(50) NOT NULL PRIMARY KEY,
    password VARCHAR(500) NOT NULL,
    enabled BOOLEAN NOT NULL
);

CREATE TABLE authorities (
    username VARCHAR(50) NOT NULL,
    authority VARCHAR(50) NOT NULL,
    CONSTRAINT fk_authorities_users FOREIGN KEY (username) REFERENCES users (username)
);

CREATE UNIQUE INDEX ix_auth_username ON authorities (username, authority);

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

&lt;/div&gt;



&lt;p&gt;The sole adjustment needed to transition from &lt;code&gt;InMemoryUserDetailsManager&lt;/code&gt; to &lt;code&gt;JdbcUserDetailsManager&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    @Bean
    public UserDetailsService userDetailsService(DataSource dataSource) {
        return new JdbcUserDetailsManager(dataSource);
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration is effective for applications that stick to Spring Security's standard table schema. But, if you need to customize (like using an email for login instead of a username), implementing a custom UserDetailsService offers the necessary adaptability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom UserDetailsService with a Customer Entity
&lt;/h2&gt;

&lt;p&gt;Lets add custom CustomUserDetailsService to the provider. In the AuthenticationProvider set the custom service using setUserDetailsService&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Configuration
public class UserConfig {
    private final CustomUserDetailsService userDetailsService;

    public UserConfig(CustomUserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }


    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(userDetailsService);
        provider.setPasswordEncoder(passwordEncoder());
        return provider;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Custom service&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Service
public class UserDetailsService implements org.springframework.security.core.userdetails.UserDetailsService {
    private final IUserRepository userRepository;

    public UserDetailsService(IUserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        var user = this.userRepository.findByUsername(username);
        if (user.isEmpty())
            throw new UsernameNotFoundException("User not found");
        return user.get();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Repository&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Repository
public interface IUserRepository extends CrudRepository&amp;lt;UserProfile, UUID&amp;gt; {
    Optional&amp;lt;UserProfile&amp;gt; findByUsername(String username);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Entity&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Entity
@Table(name = "userprofile")
public class UserProfile extends AuditTrail implements UserDetails, CredentialsContainer {
    private String firstName;
    private String lastName;
    private String username;
    private String email;
    private String password;

    @OneToMany(mappedBy = "userProfile", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
    private Collection&amp;lt;UserRole&amp;gt; userRoles;

    @OneToOne
    @JoinColumn(name = "tenant_id")
    private Tenant tenant;


    @Override
    public Collection&amp;lt;? extends GrantedAuthority&amp;gt; getAuthorities() {
        // You can write custom logic here to return roles/permissions
        return null;
    }

    @Override
    public String getPassword() {
        return this.password;
    }

    @Override
    public String getUsername() {
        return getEmail();
    }

    @Override
    public boolean isAccountNonExpired() {
        return UserDetails.super.isAccountNonExpired();
    }

    @Override
    public boolean isAccountNonLocked() {
        return UserDetails.super.isAccountNonLocked();
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return UserDetails.super.isCredentialsNonExpired();
    }

    @Override
    public boolean isEnabled() {
        return UserDetails.super.isEnabled();
    }

    @Override
    public void eraseCredentials() {
        this.password = null;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the security filter we have to tell spring security to use this service &lt;/p&gt;

&lt;p&gt;&lt;code&gt;.clientAuthentication(clientAuth -&amp;gt; clientAuth.authenticationProvider(authenticationProvider))&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    @Bean
    @Order(1)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
        OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = OAuth2AuthorizationServerConfigurer.authorizationServer();
        http
                .cors(Customizer.withDefaults())
                .authorizeHttpRequests(authz -&amp;gt; authz
                        .requestMatchers(ALLOW_LIST).permitAll()
                        .requestMatchers("/**", "/oauth2/jwks/").hasAuthority("SCOPE_keys.write")
                        .anyRequest()
                        .authenticated())
                .securityMatchers(matchers -&amp;gt;
                        matchers.requestMatchers(antMatcher("/oauth2/**"), authorizationServerConfigurer.getEndpointsMatcher()))
                .with(authorizationServerConfigurer, (authorizationServer) -&amp;gt;
                        authorizationServer
                        .oidc(Customizer.withDefaults())
                        .clientAuthentication(clientAuth -&amp;gt; clientAuth.authenticationProvider(authenticationProvider)))    // Enable OpenID Connect 1.0
                .exceptionHandling((exceptions) -&amp;gt; exceptions
                        .defaultAuthenticationEntryPointFor(
                                new LoginUrlAuthenticationEntryPoint("/login"),
                                new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
                        ))
                .oauth2ResourceServer((oauth2) -&amp;gt; oauth2.jwt(Customizer.withDefaults()));
        return http.build();
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Here, you have two robust choices for handling authentication:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;JdbcUserDetailsManager&lt;/strong&gt;: A straightforward option if your application aligns with Spring's default schema.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom UserDetailsService&lt;/strong&gt;: Provides the flexibility to manage special fields and roles.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No matter if you choose JdbcUserDetailsManager or decide to implement a custom UserDetailsService, both will equip your application with a scalable, database-supported authentication system.&lt;/p&gt;

</description>
      <category>spring</category>
      <category>springsecurity</category>
      <category>java</category>
      <category>springauthorizationserver</category>
    </item>
  </channel>
</rss>
