Search code examples
javamavenjbosssingletonjava-ee-7

NPE on a @Inject field for a @Singleton class


I have come across a curious scenario in which any attempt of using CDI for managing a singleton has resulted in failure no matter the solutions I tried, and I am not even sure at this point if this is something feasable/that can be done, hence the reason I am posting here.

I'll try to explain to my best the current scenario and I would like to know whether my intention can be done, and if yes, by what way, and if not, is there some alternative to "upgrading" the singleton instances in that jar. If I have missed details/parts of the code, please let me know, as this is something a little bit unusual and I do not if I provided everything needed.

Background:

  • JBoss 7.2 EAP being used as a server;
  • One big Maven project, the parent pom file only acts as a pom to provide dependencies/configurations in maven for all the other sub-projects, which are of type ejb/ear/war mainly.
  • Custom module being used in the JBoss that provides a number of the dependencies for some projects, so it is just a folder full of jar files, including custom jars that are being used throughout the other projects.
  • testing-purposes-ear.ear being deployed to the server, that contains an testing-ejb and testing-war projects.
  • The war project has a provided dependency to one of the jar files from the custom module (the jar is actually just another sub-project of the main project), and at compile and runtime it was resolving okay and there no were issues (exactly the same as using a maven dependency in my opinion);
  • Until now, the war project was calling the singleton instances from that jar (acting purely as business logic) by the getInstance() method, and everything was working fine.

Intention:

  • Removing all the private constructors/getInstance() from the jar for the singleton classes and annotating them with @Singleton in the hopes of changing all the calls in the war project to another way, by using the @Inject, instead of using the getInstance() method.

Steps taken:

  • For one single class, removed the private constructor, getInstance(), created a new interface for the class to expose only certain methods, and the singleton class was implementing the new interface and was marked with the @Singleton annotation from javax.inject.

  • In the war project, removed the getInstance() call, and set the variable with @Inject as a field in the class that was using it, like so:

    @Inject
    private Management managementInterface;
    ...
    void someMethod(){
        managementInterface.performBusinessLogic();
    }
    
  • This resulted in a NPE being thrown when calling performSomeBusinessLogic(), for which I found several answers, so I have added a beans.xml to that jar with discovery-mode=all, tried the @Singleton from javax.ejb, but nothing worked to make it possible to inject that singleton.

  • I have searched quite some time to see how this could be done, but most of the scenarios depicted here/on google are about inter-ejb communication between application deployed in the same container, but this is not the same : we are trying to use CDI to inject a singleton from a jar file that is not an ejb/war project, but rather a simple java library.

Things to consider:

  • That singleton class was previously used in multiple ejb/war projects with no issues, so the fact that it was put as a custom module in JBoss and a standalone jar means there really is one only instance of that class for all the calling ejb/war projects, and we avoid the whole context/class-loading typical issues that come with this stuff.

  • That singleton exposed quite a lot of methods, some of which were actually better not exposed, so I would like to put an interface before it so the calling clients will only have access to some methods, but not all of them.

  • The jar file has access to a lot of libraries/dependencies due to the structure of the project and the way the dependencies are managed, so the intention was to drop the old way of creating and using singletons and migrating it to something more modern.

  • The IDE (Intellij) is able to resolve the dependency without issues, as can be seen in the following attachment, on clicking the blue mark icon it leads to the class that is marked with @Singleton and that's implementing that interface. dependency being solved by the IDE

Question:

  • Is it possible to change the singletons from that jar project from the classic way to a way that uses CDI from the server? In this regards, something by the use of annotations would be best, but if there are any other solutions available, I'm open to them.

Solution

  • If the Management class is in a jar dependency (provided from JBoss or packaged inside the ear) is not viewed as a CDI bean. It must be insiede an EJB/CDI module packaged at the same level as testing-ejb.

    Another way is to use @Remote EJB annotation and deploy the jar as a library, not a simple dependecy.

    It's not clear (for me) the purpose of this refactoring but if is to avoid to instantiate the class manually on every bean that need it you could write a simple producer:

    @Singleton
    public class ManagementProvider() {
        @Produces
        public Management getInstance() {
            return Management.getInstance();
        }
    }
    

    Then you can use @Inject anytime you want!