Robin
Robin

Reputation: 67

Accessing injected properties results in NullPointerException

This is the first time for me working with Quarkus and CDI and I think I'm getting it wrong.

In my application users can download some files. I'd like to control how many files are being downloaded simultaneously. I thought I could do this by creating a bean with @ApplicationScoped annotation, forcing instantiation at startup, and then injecting it wherever I need to know how many files are currently downloading.

This is what I managed to do:

@ApplicationScoped
public class DownloadState {
    private int downloadingFiles;

    void startup(@Observes StartupEvent event) {
        setDownloadingFiles(0);
        System.out.println("downloading files: " + downloadingFiles);
    }

    public int getDownloadingFiles() {
        return downloadingFiles;
    }

    public void setDownloadingFiles(int downloadingFiles) {
        this.downloadingFiles = downloadingFiles;
    }

    public void incrementDownloadingFiles() {
        downloadingFiles++;
    }

    public void decrementDownloadingFiles() {
        downloadingFiles--;
    }
}

Doing this I can see the log at startup saying "downloading files: 0", so I know the class has been instantiated.

I try to access the number of downloading files here:

public class Downloader {
    private static final Logger LOG = Logger.getLogger(Downloader.class);

    @Inject
    DownloadState downloadState;

    private Dotenv dotenv = Dotenv.configure()
        .directory("../")
        .filename(".env.local")
        .load();

    private String fileName = dotenv.get("DOWNLOAD_PATH");
    private String url;

    public Downloader(String url, String fileName) {
        this.url = url;
        this.fileName += fileName;
    }

    public void downloadProduct() {

        LOG.info("Downloading Files: " + downloadState.getDownloadingFiles());
        //...
    }
}

Whenever downloadProduct is called a NullPointerException is thrown on the line LOG.info("Downloading Files: " + downloadState.getDownloadingFiles());

Am I getting CDI totally wrong? Any help is really appreciated, thank you in advance.

Upvotes: 2

Views: 2007

Answers (1)

Ladicek
Ladicek

Reputation: 6607

I assume you're calling the Downloader constructor directly -- in which case, indeed no injection will happen and your downloadState will be null. Dependency injection is "contagious" -- you have to use it everywhere.

In your case, I'd probably just make Downloader also @ApplicationScoped, inject it everywhere you use it, and probably move the url and fileName parameters from constructor to downloadProduct. Actually at that point, the number of downloadingFiles could also be in Downloader. (Also note that it can be accessed from multiple threads -- so I'd probably use an AtomicInteger for downloadingFiles.)

All in all, something like this:

@ApplicationScoped
public class Downloader {
    private static final Logger LOG = Logger.getLogger(Downloader.class);

    private final AtomicInteger downloadingFiles = new AtomicInteger(0);

    private final String downloadPath = Dotenv.configure()
        .directory("../")
        .filename(".env.local")
        .load()
        .get("DOWNLOAD_PATH");

    public void downloadProduct(String url, String fileName) {
        String path = downloadPath + fileName;

        int current = downloadingFiles.incrementAndGet();
        LOG.info("Downloading Files: " + current);
        //...
    }
}

Upvotes: 4

Related Questions