Search code examples
javajakarta-eehelidon

Using ConfigProperty in manually instantiated classes in JakartaEE / Helidon / Microprofile


I have a small application in Helidon start. It is mostly a REST interface, but I also want to start some background monitoring / logging on startup.

I would like that monitoring to be activated / deactivated by config. The issue I am facing is that the config is not being picked up if my class is instantiated manually.

Here is a very short code snippet :

Starting the application

public class Main {

    private Main() { }

    public static void main(final String[] args) throws IOException {
        Server server = startServer();

        CellarMonitoring monitoring = new CellarMonitoring();
        monitoring.start();
    }

    static Server startServer() {
        return Server.create().start();
    }
}

Starting monitoring or not based on Configuration :

package nl.lengrand.cellar;

import org.eclipse.microprofile.config.inject.ConfigProperty;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;

public class CellarMonitoring {

    @Inject
    @ConfigProperty(name = "monitoring.enabled", defaultValue = "true")
    private volatile boolean monitoringEnabled; <= Always false

    public void start(){
        if(monitoringEnabled) {
            System.out.println("Monitoring enabled by config. Starting up");
        }
        else System.out.println("Monitoring disabled by config");
    }
}

This code will always return "Monitoring disabled by config", whatever I do.

Accessing the config directly like described in the documentation is not really an option either since the onStartup method will never be fired.

What is the proper way to inject a class in my server so it can access the config as desired?


Solution

  • Your question is actually about CDI.

    In order for any kind of dependency injection to work with CDI, CDI must instantiate the thing to be injected. In this case, you instantiate the thing to be injected, so CDI never "sees" it, so it is never injected.

    I am speculating here, but I'm guessing your use case is really just: "I'd like my CellarMonitoring component to be notified when CDI comes up. How do I do that?"

    There are many answers to that question on this site and elsewhere. Essentially you take advantage of the fact that CDI will fire an event notifying any interested listeners in the initialization of the application scope. The application scope is effectively the lifespan of the application itself, so you can think of it as a startup event.

    A full CDI tutorial is beyond the scope of this question and answer, but, to cut to the chase, here's a way to do it. I have had to make various assumptions, such as that you want CellarMonitoring to be singleton-like:

    @ApplicationScoped
    public class CellarMonitoring {
    
      @Inject
      @ConfigProperty(name = "monitoring.enabled", defaultValue = "true")
      private volatile boolean monitoringEnabled; // <= Always false
    
      public void start() {
        if (monitoringEnabled) {
          System.out.println("Monitoring enabled by config. Starting up");
        } else {
          System.out.println("Monitoring disabled by config");
        }
      }
    
      private void onStartup(@Observes @Initialized(ApplicationScoped.class) final Object event) {
        // The container has started.  You can now do what you want to do.
        this.start();
      }
    
    }