Can't get @Singleton to do anything

cdi, jakarta-ee, java, jboss7.x, singleton

Solution

After some research, it turns out that Atmosphere provides (currently broken) hooks for this kind of functionality. That means it IS possible to simply use your normal annotations and have it work with Atmosphere, despite the 'foreign' instantiation.

If you know where your code will be deployed, you can simply overwrite the default noop InjectorProvider class from Atmosphere by having a class with the same name in the same package and having it provide the proper Injector.

I am adding code for JBoss AS7 at the end of this answer, as well as links to code that is supposed to work on Google Guice and Spring, which I have not tested myself.

If you need your code to work on multiple platforms, you will probably need to figure out how to detect what you are running on and then return the appropriate Injector. Since I'm not very familiar with Guice and Spring, I'll leave that exercise to the reader.

Dirty 'first draft' code for JBoss AS7 (remember that this has to go into the declared package to overwrite the default noop provider):

package org.atmosphere.di;

import java.util.NoSuchElementException;
import java.util.ServiceLoader;

import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.InjectionTarget;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class InjectorProvider {

    private InjectorProvider() {}

    public static Injector getInjector() {
        return LazyProvider.INJECTOR;
    }

    private static final class LazyProvider {
        private static final Injector   INJECTOR;

        static {
            Injector injector = new Injector() {
                @Override public void inject(final Object o) {
                    try {
                        final BeanManager bm = (BeanManager) new InitialContext().lookup("java:comp/BeanManager");
                        final CreationalContext cc = bm.createCreationalContext(null);
                        final InjectionTarget it = bm.createInjectionTarget(bm.createAnnotatedType(o.getClass()));
                        it.inject(o, cc);
                        cc.release();
                    } catch (final NamingException e) {
                        e.printStackTrace();
                    }
                }
            };
            try {
                injector = ServiceLoader.load(Injector.class).iterator().next();
            } catch (final NoSuchElementException e) {}
            INJECTOR = injector;
        }
    }
}

Please note that the wrapping code is taken from the Atmosphere code base, and is a very bad way to do Singletons in Java. You probably don't want to use this 'as is' in production.

Guice injector, untested: https://github.com/Atmosphere/atmosphere/blob/atmosphere-1.0.x/extras/guice/src/main/java/org/atmosphere/guice/GuiceInjector.java

Spring injector, untested: https://github.com/Atmosphere/atmosphere/blob/atmosphere-1.0.x/extras/spring/src/main/java/org/atmosphere/spring/SpringInjector.java

Problem

Running on JBoss AS7, I have this: ``` import javax.inject.Singleton; @Singleton public class Connections { private final List<AtmosphereResource> connections = new ArrayList<AtmosphereResource>(); public void add(AtmosphereResource event) { connections.add(event); } } ``` and this: ``` import javax.inject.Inject; public class PubSubAtmosphereHandler extends AbstractReflectorAtmosphereHandler { @Inject private Connections connections; @Override public void onRequest(AtmosphereResource event) throws IOException { [...] connections.add(event); // <--- } ``` NPE on the designate line. After reading countless pages and examples, this is one of the ways that is repeated dozens of times, yet it doesn't work. I have the empty beans.xml, placed in my WEB-INF. What am I missing here ?

Original source