History of a bug

Spring @ControllerAdvice order and precedence

Rédigé par gorki Aucun commentaire

Problem :

I added a second @ControllerAdvice to handle my 404 pages in addition to an existing @ControllerAdvice that was handling a REST Api for the backend part.

My newer controller advice has : 

    public ModelAndView handleError404(HttpServletRequest request, Exception e) {
        if (request.getRequestURI().startsWith("/api")) {
            throw new ResourceNotFoundException();
        } else {
            ModelAndView mv = new ModelAndView();
            return mv;

The existing one has : 

    public ErrorDTO handleException(Exception e) {
        log.error(e.getMessage(), e);
        return new ErrorDTO(e.getMessage(), "E01");

On dev environment, it works.

On production environment, it fails : only the REST API exception handler was used.

A remote debug show me that I was in : @ExceptionHandler(Exception.class)

the general exception handler, and the exception thrown is NoHandlerFoundException

Solution :

I tried this solution : 

  • Use @Order on each class
  • as seen here

But it also fails and as noted in the answers : 

I also found in the documentation that :



protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception)

Find an @ExceptionHandler method for the given exception. The default implementation searches methods in the class hierarchy of the controller first and if not found, it continues searching for additional @ExceptionHandler methods assuming some @ControllerAdvice Spring-managed beans were detected. Parameters: handlerMethod - the method where the exception was raised (may be null) exception - the raised exception Returns: a method to handle the exception, or null

So I merged the both classes and it works.

But it's not really clear : 

  • Older exception handler is not in the class hierarchy
  • I did not check if my latest exception handler was well detected on production (code is the same and it works in dev)

So not yet the final word, but too late for today :)




Postgresql pg_upgrade failed : role <XXX> unknown

Rédigé par gorki Aucun commentaire

Problem :

When migrating a database, pg_upgrade failed with “could not connect, role <XXX> is unknown”.

My database was created with another user (postgresql)

Then transfered to another user (let's call it john)

The login authentication was password only until upgrade, where I put local to trust for upgrade.

The new destination database was created by john.

Solution :

The pg_upgrade can not use : 

  • an old database with super role postgres
  • a new database with super role john

The option “-U <username>” is applied to the both, so there is always one which is wrong.

So rename the old database super role to john (thanks to internet).

  1. start the old database, pg_upgrade give the command in the log but !!
  • Be careful ! there is a hidden option “-b” which prevent any modification :) remove it and all is ok
"/home/myuser/postgresql-9.6.2/bin/pg_ctl" -w -D "/home/myuser/database-introscope-9.6" -o "-p 50432  -c listen_addresses='' -c unix_socket_permissions=0700 -c unix_socket_directories='/home/myuser'" start

Connect with : 

/home/myuser/postgresql-9.6.2/bin/psql -p 50432 -h /home/myuser -U postgres -d postgres

Check the super user role name : 

SELECT rolname FROM pg_roles WHERE oid = 10;

Create another super user


Quit, connect with spideman, still on postgres database

/home/myuser/postgresql-9.6.2/bin/psql -p 50432 -h /home/myuser -U spideman -d postgres

Rename the original role :

alter role postgres rename to john;

Check the connection :

/home/myuser/postgresql-9.6.2/bin/psql -p 50432 -h /home/myuser -U john -d postgres

Drop spiderman role : 

DROP ROLE spiderman;

I learn to side tricks here : 

  • -h <path> : give the path to the unix socket connection
  • -b option : use for binary upgrade mode on pg_ctl (didn't see the option in the documentation)
  • Owner role has value OID=10




Relocatable postgresql

Rédigé par gorki Aucun commentaire

Problem :

I need a relocatable / portable version of postgresql.

Disclaimer : if possible, use the OS distribution, it will be more up-to-date and sure.

That thing said : 

  • I downloaded the source
  • Compile it on my computer (Debian / Trixie / Sid)
  • Package and run

Fail ! Obviously but here is why : 

  1. Shared library are related to an absolute path
  2. Glibc version on my computer is more recent than target server

Solution :

Two problems here and a few useful commands :

  • ldd <program name>
    • Will give you if the program is static or dynamic
    • If dynamic, the list of libraries and the searched path, example (libpq is a library from postgres)
 libpq.so.5 => /lib/x86_64-linux-gnu/libpq.so.5 (0x00007f29f9786000)
  • readelf -d <program name> : information on execution
0x0000000000000001 (NEEDED)             Shared library : [libpq.so.5]
0x0000000000000001 (NEEDED)             Shared library : [libm.so.6]
0x0000000000000001 (NEEDED)             Shared library : [libc.so.6]
0x000000000000001d (RUNPATH)            Library runpath :[/home/myuser/projects/packaging/postgresql/output/lib]
0x000000000000000c (INIT)               0x6000
0x000000000000000d (FINI)               0x162a0
  • objdump -T <program name> | grep GLIBC : list dependencies on GLIBC


Once I had this information : 

  • I compile postgresql on the target system : GLIBC 2.34, 2.33 were not available so postgresql compile without linking it
  • I change the runpath of the executables with these commands :
# extract postgres sources
./configure --prefix=/home/myuser/postgresql/output --without-icu --without-readline --without-zlib --disable-rpath
export LD_RUN_PATH='$ORIGIN/../lib'
make install

It generates a postgresql version in  /home/myuser/postgresql/output

The readelf commands returns : 

 0x000000000000000f (RPATH)              Library rpath: [$ORIGIN/../lib]

So I was now able to package it.



AtomicLong vs Long

Rédigé par gorki Aucun commentaire

Problem :

I searched for an simple example of AtomicLong vs Long problem.

Thanks to Ecosia IA (ChatGPT), here is a simple example

Solution :

Result is : 

  • Counter value (Long): 34938
  • Counter value (AtomicLong): 100000

Here is the code :

package tools;
import java.util.concurrent.atomic.AtomicLong;

public class AtomicLongExample {
    private static Long counter = 0L;
    private static AtomicLong atomicCounter = new AtomicLong(0);

    public static void main(String[] args) {
        // Create and start multiple threads
        Thread[] threads = new Thread[100];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(new CounterRunnable());

        // Wait for all threads to finish
        for (Thread thread : threads) {
            try {
            } catch (InterruptedException e) {

        System.out.println("Counter value (Long): " + counter);
        System.out.println("Counter value (AtomicLong): " + atomicCounter.get());

    static class CounterRunnable implements Runnable {
        public void run() {
            // Increment the counter using Long variable within a loop
            // This may lead to race conditions and incorrect results
            for (int i = 0; i < 1000; i++) {

            // Increment the counter using AtomicLong within a loop
            // This ensures atomicity and thread-safety
            for (int i = 0; i < 1000; i++) {

SpringBoot OSGI and templated OSGI modules with external constrant Jetty

Rédigé par gorki Aucun commentaire

Problem :

A very specific and particular problem.

I want to deploy  a SpringBoot application, in an OSGI environment, in a external Jetty server…

So I found solution do deploy Springboot in OSGI : here or here. Very good by the wat.

And also to deploy Springboot as war for an external Tomcat…

Too easy, my external Jetty server was rejecting Servlet 3.0 application startup… (hard coding for : 

context.setAttribute("org.eclipse.jetty.containerInitializers", initializers);

Yes, it's better, starting a 1,25 days of try and success !

Solution :

My first web application was deployed with Spring Dispatcher Servlet with a BundleActivator

My second web application was deployed with Spring DispatcherServlet AND a SpringBootApplication intialized with the ressource loader of the BundleActivator (as in the OSGI example) : component-scan is working ! 

My third web application was switching to an initialization Servlet 3.0 with an extension of ServletContainerInitializer, thanks to harded jetty configuration, it's not working

My third and a half remove all web.xml content to keep only a context listener as my Jetty was doing nothing : 

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"

    <!-- ServletContainerInitializer can not be used here -->
    <!-- XXXXXXX is putting in hard : context.setAttribute("org.eclipse.jetty.containerInitializers with only Jasper -->

    <!-- At the end, it's possible to load SpringBoot with this context listener -->


My fourth web application was trying to load the Springboot application file as a YAML file (why so simple !), resolved with a “hack” :

  • The ServletContext has a way to store the configuration directory, during the initialization, I saved it somewhere : HpaContextLoaderListener.getConfigDirectory() 

The listener loader : 

package com.xxxxx.extension;

import org.eclipse.jetty.webapp.WebAppContext;
import org.springframework.web.context.WebApplicationContext;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletException;
import java.io.File;
import java.nio.file.Path;

public class MyContextLoaderListener implements ServletContextListener {

    private static File configDirectory;

    public static File getConfigDirectory() {
        return configDirectory;

    public void contextInitialized(ServletContextEvent event) {
        ServletContext servletContext = event.getServletContext();
        MySpringBootHexagonActivator app = new MySpringBootHexagonActivator();

        try {
            configDirectory = servletContext.getAttribute("introscope.em"));
            InstantiateHelper.getValueByMethod(servletContext, "setExtendedListenerTypes", new Class[]{boolean.class}, true);

        } catch (ServletException e) {
            throw new RuntimeException(e);

    public void contextDestroyed(ServletContextEvent event) {


public class MySpringBootHexagonActivator extends SpringBootServletInitializer {

    protected SpringApplicationBuilder createSpringApplicationBuilder() {
        SpringApplicationBuilder builder = super.createSpringApplicationBuilder();
        Map<String, Object> properties = new HashMap<>();
        Path propertyFile = Path.of(MyContextLoaderListener.getConfigDirectory().getPath(), "application-extension.yml");
        properties.put("spring.config.location", propertyFile.toString());

        return builder;

The HpaHexagonActivator.getResourceResolver() is the trick from SpringBoot OSGI example to load classes from Bundle classpath. I store the ressources resolver in a static variable that I can access from everywhere.

A word on that one : 

InstantiateHelper.getValueByMethod(servletContext, "setExtendedListenerTypes", new Class[]{boolean.class}, true);

I'm in OSGI, Jetty controls the listener class, and Springboot is unknown. You can authorized additional listeners but in my case, Jetty is not exported by the other OSGI package, not able to call it simply so… reflection : InstantiateHelper is basically a helper class to call methods by reflection.


At the end : 

  1. Springboot starts
  2. Springboot scans package offered by BundleActivator ressource locator
  3. Springboot can use an external configuration file, in YAML
  4. Springboot starts REST & Database
  5. It was hard.

Of course, lot of tries between these big steps !

The goal is to extend a 3rd party product without having source code.

Fil RSS des articles