Category Archives: Java

Maven Booted My Spring App Fine. IntelliJ Didn’t. Here’s the One-Checkbox Fix.

The setup: A Spring Boot service that booted perfectly with mvn spring-boot:run, but crashed immediately when launched from IntelliJ’s Run/Debug configuration — same code, same machine, same dependencies.

The error:
  Exception in thread "main" org.springframework.beans.factory.BeanDefinitionStoreException:
  Failed to read candidate component class: ...
  Caused by: java.lang.IllegalArgumentException: Could not find class [com.azure.cosmos.models.CompositePathSortOrder]
  Caused by: java.lang.ClassNotFoundException: com.azure.cosmos.models.CompositePathSortOrder

Spring’s classpath scanner hit an annotation referencing a class that, according to the JVM, didn’t exist.

First instinct — wrong: This looked like a version conflict. My local Maven repo had several versions of the library in question cached side by side, so a mismatch seemed like the obvious suspect. It wasn’t. Every version was internally consistent; the class was exactly where it should be.
Finding the real cause: Instead of guessing further, I asked Maven directly where this dependency actually comes from:

mvn dependency:tree -Dincludes=

The output told the whole story in one line:

  my-app
  \- some-transitive-library:jar:x.y.z:provided
     \- the-missing-library:jar:x.y.z:provided

provided scope. That’s Maven’s way of saying: “this dependency is needed to compile against, but something else — a container, an app server, the deployment environment — supplies it at runtime. Don’t bundle it.”

Why the two tools disagreed:

  • mvn spring-boot:run treats the JVM it launches as that “runtime environment,” so it includes provided-scope dependencies on the exec classpath.
  • IntelliJ’s Run Configuration does not include provided-scope dependencies by default — and that’s the correct, intentional behavior for the scope’s contract. It’s not a bug; it’s IntelliJ doing exactly what provided is supposed to mean.

    Same POM, same dependency graph, two different — both legitimate — interpretations of one Maven scope.
    The fix, once the cause was clear, took ten seconds: open the Run/Debug Configuration, click Modify options, and enable:

       
      "Add dependencies with 'provided' scope to classpath"
    

    No POM edits. No version pinning. No cache invalidation. Rerun — boots clean.

    The lesson, generalized: When a Maven-launched app and an IDE-launched app disagree on a missing class that genuinely exists in your dependency tree, don’t reach for version conflicts first. Check scope. mvn dependency:tree -Dincludes= will show you exactly which dependency brought a class in and under what scope — that single fact usually explains the whole discrepancy, and the fix is often a checkbox, not a rebuild.

  • Java 25 Installed but Java 17 Still Active on macOS: Fixing jenv and SDKMAN Conflicts

    I recently installed OpenJDK 25 using Homebrew. The installation completed successfully, but my terminal continued to report an older Java version.

    The root cause turned out to be a conflict between jenv and another Java version manager. This post documents the diagnosis and the solution.

    Install OpenJDK

    brew install openjdk@25

    Homebrew instructs you to register the JDK with macOS:

    sudo ln -sfn \
    /opt/homebrew/opt/openjdk@25/libexec/openjdk.jdk \
    /Library/Java/JavaVirtualMachines/openjdk-25.jdk

    Verify that macOS recognizes the JDK

    Check that the JDK is visible to macOS:

    /usr/libexec/java_home -V

    You should see an entry for OpenJDK 25.

    If macOS can detect the JDK, the installation itself is successful.

    Add the JDK to jenv

    By default, jenv does not automatically discover newly installed JDKs.

    Add the JDK using:

    jenv add "$(/usr/libexec/java_home -v 25)"

    Verify that it has been registered:

    jenv versions

    You should see entries similar to:

    system
    25
    25.0
    openjdk64-25

    Select Java 25

    jenv global 25
    jenv rehash

    Confirm the selected version:

    jenv version

    If Java is still not changing

    The most common cause is another Java installation appearing earlier in your PATH.

    Check which executable is actually being used:

    which java

    If the result is not a jenv shim, then jenv is not controlling your shell.

    Initialize jenv

    Add the following near the end of your shell startup file:

    export PATH="$HOME/.jenv/bin:$PATH"
    eval "$(jenv init -)"

    Reload your shell:

    source ~/.zshrc

    or simply start a new terminal.

    Enable JAVA_HOME management

    If JAVA_HOME still points to another installation, enable the export plugin:

    jenv enable-plugin export

    Restart your shell afterwards.

    Verify everything

    which java
    java --version
    jenv version
    echo $JAVA_HOME

    Expected results:

    • which java points to a jenv shim.
    • java --version reports OpenJDK 25.
    • jenv version reports version 25.
    • JAVA_HOME is managed by jenv.

    Key takeaway

    Installing a JDK is only one part of the process. If multiple Java version managers are installed, whichever one appears first in your shell configuration controls the Java executable and JAVA_HOME.

    If Java refuses to switch versions:

    1. Verify that macOS recognizes the JDK.
    2. Add it to jenv.
    3. Select it with jenv global.
    4. Ensure the jenv shims appear before other Java installations in your PATH.
    5. Enable the jenv export plugin so that JAVA_HOME stays synchronized.

    From One Big App to Many Small Ones: A Developer’s Guide to Containers

    Picture this: you’ve built a successful web application that started small but has grown into something amazing. Users love it, your team has expanded, and everything seems great. But there’s a problem lurking beneath the surface. Every time you want to add a new feature or fix a bug, it takes forever. Deploying updates feels like performing surgery on a patient who’s wide awake. Sound familiar?

    If you’re nodding your head, you’re dealing with what developers call a “monolith” – an application where everything is bundled together in one massive codebase. While monoliths work great when you’re starting out, they can become a real headache as your app grows. The good news? There’s a proven way to solve this problem using something called containers.

    What’s the Problem with Big Applications?

    Think of a monolithic application like a huge department store where everything is connected. The clothing section shares the same checkout system as electronics, the inventory system controls everything from shoes to smartphones, and if you want to renovate the toy section, you might accidentally break the jewelry department.

    In software terms, this means:

    • When one part of your app breaks, it can bring down everything else
    • Adding new features requires testing the entire application
    • Scaling becomes expensive because you have to scale everything, even if you only need more power for one feature
    • Different teams end up stepping on each other’s toes

    Enter Containers: Your App’s New Best Friend

    Containers are like moving each department of that massive store into its own building. Each department (or service) can operate independently, but they can still communicate with each other when needed. If the toy store needs renovation, the electronics store keeps running without interruption.

    In technical terms, a container packages your application code along with everything it needs to run – like a lunch box that contains not just your sandwich, but also the plate, napkin, and utensil you need to eat it.

    The Step-by-Step Journey: From Chaos to Order

    1. Take a Good, Hard Look at What You Have

    Before you start tearing apart your application, you need to understand what you’re working with. This is like creating a detailed floor plan of that massive department store before you start moving things around.

    Spend time examining your codebase to identify different functional areas. Most applications naturally group into sections like:

    • User accounts and login systems
    • Payment processing
    • Email notifications
    • Data reporting
    • Content management

    Draw these relationships out on paper or in a diagramming tool. You’ll be surprised how much this simple exercise reveals about your application’s structure.

    2. Put Your Entire App in a Container First

    Here’s where most people make a mistake: they immediately try to break everything apart. Don’t do that. Instead, take your entire monolithic application and put it in a container first.

    This is like moving your entire department store into a standardized building before you start separating departments. It solves a huge problem called “environment inconsistency” – the dreaded “it works on my computer but not on yours” syndrome.

    When your app runs the same way on your laptop, your colleague’s computer, and your production servers, you eliminate countless headaches and mysterious bugs.

    3. Pick Your First Target Carefully

    Now comes the fun part: choosing which piece to extract first. This decision is crucial and should be strategic, not random.

    Look for parts of your application that are:

    • Self-contained (they don’t depend heavily on other parts)
    • Relatively simple
    • Not critical to your core business logic

    Great first candidates include:

    • Authentication systems (login/logout functionality)
    • Email notification services
    • File upload handlers
    • Search functionality

    Successfully extracting your first service is like winning your first game – it builds confidence and teaches you the process for future extractions.

    4. Bring in the Orchestra Conductor

    As you create more containers, managing them manually becomes like trying to conduct a symphony orchestra by shouting instructions. You need a proper conductor, and in the container world, that’s Kubernetes.

    Kubernetes is a platform that automatically handles:

    • Starting and stopping your containers
    • Distributing traffic between multiple copies of the same service
    • Restarting failed containers
    • Scaling services up or down based on demand

    For beginners, consider starting with simpler alternatives like Docker Compose for development, then moving to managed Kubernetes services offered by cloud providers like Google Cloud, AWS, or Microsoft Azure.

    5. Automate Everything from Day One

    One of the biggest mistakes teams make is leaving deployment as a manual process. This is like insisting that every product in your store be moved by hand instead of using conveyor belts and automated systems.

    Set up automated pipelines that:

    • Test your code automatically when you make changes
    • Build container images without human intervention
    • Deploy to testing environments instantly
    • Notify you if anything goes wrong

    This automation eliminates human error and makes deployments so routine that they become boring – which is exactly what you want.

    6. Untangle the Database Web

    Databases are often the trickiest part of breaking up a monolith. In our department store analogy, this is like having a single cash register system that every department has been modifying over the years.

    The key principle is simple: each service should own its data. Instead of letting multiple services directly access the same database tables, establish clear boundaries. If Service A needs data from Service B, it should ask politely through an API rather than sneaking into Service B’s database.

    You don’t necessarily need separate physical databases immediately, but you must enforce these ownership rules in your code. For shared data like user sessions, move them to dedicated systems like Redis that are designed for sharing.

    7. Make Your System Observable

    When you had one big application, finding problems was like debugging issues in a single room. With multiple services, it’s like troubleshooting problems across an entire shopping mall. You need security cameras, intercoms, and monitoring systems everywhere.

    Implement comprehensive observability from the start:

    • Logging: Ensure every service writes detailed logs about what it’s doing
    • Monitoring: Track metrics like response times, error rates, and resource usage
    • Health checks: Each service should be able to report whether it’s healthy
    • Distributed tracing: Follow requests as they travel between services

    Tools like Prometheus for metrics collection, Grafana for dashboards, and Jaeger for tracing make this much easier than building everything from scratch.

    8. Rinse and Repeat

    With your first service successfully extracted and your tooling in place, you’re ready to continue the process. Each subsequent extraction becomes easier because:

    • You’ve learned the patterns and potential pitfalls
    • Your automation pipelines are already set up
    • Your team has gained confidence and experience
    • You have monitoring and observability systems in place

    Gradually, your monolith shrinks while your collection of focused, independent services grows.

    The Light at the End of the Tunnel

    This transformation isn’t just about technology – it changes how your entire team works. Instead of everyone working on one massive codebase and stepping on each other’s toes, different teams can own different services. The frontend team can deploy their changes without waiting for the backend team to finish theirs. The payments team can scale their service during Black Friday without affecting the recommendation engine.

    Common Pitfalls to Avoid

    Don’t try to do everything at once: The temptation to rewrite everything from scratch is strong, but it’s usually a mistake. Incremental change is safer and more sustainable.

    Don’t ignore the human element: This transformation affects your entire team’s workflow. Invest in training and make sure everyone understands the new processes.

    Don’t forget about data: Plan your database separation strategy early. It’s often the most complex part of the entire process.

    Don’t skip monitoring: In a distributed system, observability isn’t optional – it’s essential for maintaining sanity.

    Is This Journey Worth It?

    Absolutely, but only if you’re facing the problems that containers solve. If your current setup works fine and you’re not experiencing scaling or development velocity issues, there’s no rush to change.

    However, if you’re struggling with slow deployments, difficulty scaling, or teams blocking each other’s progress, containers and microservices can be transformative. You’ll gain:

    • Faster development cycles
    • Independent scaling of different components
    • Better fault isolation (one broken service doesn’t kill everything)
    • Technology flexibility (different services can use different programming languages or databases)
    • Easier team organization and ownership

    The journey from monolith to containers isn’t always smooth, but with careful planning and incremental execution, it’s entirely achievable. Every successful transformation starts with a single step, and every monolith has the potential to evolve into something better.

    Your users will notice faster updates and more reliable service. Your developers will thank you for making their work more enjoyable and productive. And you’ll sleep better knowing that a problem in one part of your system won’t bring down the entire application.

    The path forward is clear – it’s time to start containerizing.

    Developer productivity tools for iPad

    I have tried doing development on my iPad and I found some tools which I liked:
    Python – Pythonista
    Java – Jedona

    I was able to write some interesting programs while on the go on both my iPhone and especially on iPad. However I have realised that for better productivity it is better to use remote desktop solution to login into my Windows and a VNC solution to login into my MacBook.

    Windows Mobile App (Previously called RD Client)
    RealVNC Client

    I have a static IP available for my home network however one can easily use solution like noip.com to get static domain name to work using the DDNS client built in your home router. I usually configure a port forward on my router to RDP into my windows or VNC into my MacBook if the router does not support VPN server. For better security I have setup a VPN server on my router and I do a VPN connection into my home network effectively eliminating the need of opening ports / do port forwarding.

    Using the above approaches I no longer have to carry my laptops around and I use my iPad to do some work directly on iPad or via remote connection to my computers back home.

    To manage my personal Linux servers on cloud I use Blink. Combined with Mosh I have found Blink to be a solid solution to SSH into my Linux boxes and do my work.

    Blink

    This type of setup has greatly reduced the amount of hardware I need to carry around. With one iPad and an iPhone I can work on most of my hobby projects and also do some of my office work.

    Code Structure Analysis Tool

    I received a dump of Java codebase which had multiple modules and I needed to analyse it’s structure. The following tool did quite a good job:

    https://github.com/gdela/socomo

    Basically the idea is to run it inside a Java project using a maven command and it creates HTML file which denotes the high level structure of the code.

    Note: Stan4J is also a very good tool which does similar job but allows deeper analysis (upto 500 classes only)

    Throttling & Tuning Spring Boot

    One of my Spring-Boot projects was battling with overloaded CPU and unresponsive / slow server response at times when there is more traffic. I have explored and implemented caching but my problem was with excessive connections coming in and server itself becoming slow. I could have setup multiple instances and do some kind of auto-scaling but given limited budget and hardware I wanted to put in some hard limits on my Spring-Boot app as to how much traffic it can take in and when it can give up gracefully (there is no shame in rejecting traffic with HTTP status 503 if the server infrastructure is overloaded).

    I found a blog post entry from Netflix on how to tune Apache Tomcat and another article on how to tune Rest Controller code itself to implement a rudimentary Rate Limiter. I was glad to find the RateLimiter implementation in Goggle Guava library which I ultimately ended up using (for now). However I think the annotation driven RateLimiter is also a very good solution which is certainly very powerful and I will take it out for a spin sometime in near future.

    The basic lesson learnt from this exercise:
    – Tweak Tomcat and keep a watch on the acceptCount parameter which technically puts in a limit of how much traffic reaches your Rest controller.
    – Use a RateLimiter on your hot APIs (which have higher latency) and don’t let your application get abused beyond a limit.
    – Scale horizontally if the limits set above result in lot of traffic getting rejected.

    Issues Faced While Upgrading to JDK 11 and Spring Boot 2.2.5

    I had a old project which was running on JDK 8 and Spring Boot 1.5.x which I just upgraded to JDK 11 and Spring Boot 2.2.5. I faced some hiccups in the process but it is finally done and the application is up and running. I will be documenting some of the issues faced in this process.

    Spring Data
    All instances of findOne() had to be replaced with findByID() which returns Optional<Entity> reference. I ended up removing lots of null checks by leveraging the Optional features of “orElse”, “orThrow” and “ifPresent” methods provided by Optional interface. It definitely is a better way to handle null.

    JAXB runtime error
    After migration the application booted up fine but on doing some specific operation I observed this error on console

    java.lang.ClassNotFoundException: javax.xml.bind.DatatypeConverter

    It seems this class has been moved out of the core library in JDK. This error kind of stumped me as some posts on internet suggested adding the following entry in pom.xml

    <dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.1</version>
    <scope>runtime</scope>
    </dependency>

    However this didn’t help and I ended up using the following library to finally get rid of this issue:

    <dependency>
    <groupId>jakarta.xml.bind</groupId>
    <artifactId>jakarta.xml.bind-api</artifactId>
    </dependency>

    Apache POI / Tika Library
    This is another runtime issue faced during this application operation.

    WARNING: An illegal reflective access operation has occurred
     WARNING: Illegal reflective access by org.apache.poi.openxml4j.util.ZipSecureFile$1 (file:/Users/562320/.m2/repository/org/apache/tika/tika-app/1.15/tika-app-1.15.jar) to field java.io.FilterInputStream.in
     WARNING: Please consider reporting this to the maintainers of org.apache.poi.openxml4j.util.ZipSecureFile$1
     WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
     WARNING: All illegal access operations will be denied in a future release
     Jun 29, 2020 3:14:17 AM org.apache.catalina.core.StandardWrapperValve invoke
     SEVERE: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.apache.poi.openxml4j.exceptions.OpenXML4JRuntimeException: Fail to save: an error occurs while saving the package : class org.apache.poi.openxml4j.util.ZipSecureFile$ThresholdInputStream cannot be cast to class java.util.zip.ZipFile$ZipFileInputStream (org.apache.poi.openxml4j.util.ZipSecureFile$ThresholdInputStream is in unnamed module of loader 'app'; java.util.zip.ZipFile$ZipFileInputStream is in module java.base of loader 'bootstrap')] with root cause
     java.lang.ClassCastException: class org.apache.poi.openxml4j.util.ZipSecureFile$ThresholdInputStream cannot be cast to class java.util.zip.ZipFile$ZipFileInputStream (org.apache.poi.openxml4j.util.ZipSecureFile$ThresholdInputStream is in unnamed module of loader 'app'; java.util.zip.ZipFile$ZipFileInputStream is in module java.base of loader 'bootstrap')
         at java.base/java.util.zip.ZipFile$ZipFileInflaterInputStream.available(ZipFile.java:480)

    This issue got resolved after upgrading the Apache Tika, Apache POI to latest version. I also had to upgrade the apache-commons-lang package to apache-commons-lang3 package.

    Progress Bar implementation in Java for Terminal application

    Th e”\r” character basically reverts the current cursor back to the 1st column in the current line. This concept should basically work on Mac, Linux and Windows. So to test it I wrote a quick hack and it worked properly:

    public class ProgressBar {
    
        public static void main(String[] args) {
            int size = 10;
            for (int i=1; i<size; i++) {
                try {
                    Thread.sleep(500);
                    System.out.print("|" + "=".repeat(i) + ">" + " ".repeat(size-i) + "|\r");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.print("|" + "=".repeat(size) + "|\r");
        }
    }
    
    // Initial Output
    // |=>        |
    // Final Output
    // |==========|

    I got the desired output and a humble do nothing progress bar did come up on terminal. To further this idea I did some internet search and I ended up finding this repo on GitHub. It basically allows you to implement beautiful progress bars for terminal based Java application. The basic concept is the same but it provides much more functionality.

    Spring Cloud Configuration Issue

    For a new project I was trying to setup a config server to connect my services together. Although the spring-cloud-config server works fine, I am not able to make my service talk to the config-server which is really strange as I am fairly well versed with Spring Cloud and usually setting up a dev environment is a no-brainer.

    I have described my issue in stackoverflow to get help and I am debugging in parallel as well.