Configuring Atmosphere's Classes Creation and Injection - Atmosphere/atmosphere Wiki

Custom Object creation and Injection

Starting with Atmosphere 2.1+, it is possible to configure an AtmosphereObjectFactory for dependencies injection and class creation. You can define your own and pass it to the framework by defining an init-param:

<init-param>
  <param-name>org.atmosphere.cpr.objectFactory</param-name>
  <param-value> << an implementation of AtmosphereObjectFactory >> </param-value>
</init-param>

Built In Framework Support

Starting with Atmosphere 2.3.0+ Atmosphere have basic support for injection. If you are using 2.3.2+, you can also defines under META-INF/services/org.atmosphere.inject.Injectableclasses that implements the Injectable interface so the AtmosphereObjectFactory can inject those objects into your classes.

You can also enable one of the following supported frameworks by just adding the artifact on you classpath.

Using Contexts and Dependency Injection (CDI)

For CDI, Atmosphere will use the following code for injecting/creating object:

public class CDIObjectFactory implements AtmosphereObjectFactory<Object> {

    private static final Logger logger = LoggerFactory.getLogger(CDIObjectFactory.class);

    private BeanManager bm;

    public CDIObjectFactory(){
        try {
            bm = (BeanManager) new InitialContext().lookup("java:comp/BeanManager");
        } catch (NamingException ex) {
            try {
                bm = (BeanManager) new InitialContext().lookup("java:comp/env/BeanManager");
            } catch (NamingException e) {
                logger.error("{}", e);
            }
        }

        final Iterator<Bean<?>> i = bm.getBeans(AtmosphereProducers.class).iterator();
        if (!i.hasNext()) {
            throw new IllegalStateException();
        }

    }

    @Override
    @SuppressWarnings("unchecked")
    public <T, U extends T> U newClassInstance(Class<T> classType, Class<U> classToInstantiate) throws InstantiationException, IllegalAccessException {
        CreationalContext cc = null;

        try {
            final Iterator<Bean<?>> i = bm.getBeans(classToInstantiate).iterator();
            if (!i.hasNext()) {
                logger.trace("Unable to find {}. Creating the object directly.", classToInstantiate.getName());
                return classToInstantiate.newInstance();
            }
            Bean<U> bean = (Bean<U>) i.next();
            CreationalContext<U> ctx = bm.createCreationalContext(bean);
            U dao = (U) bm.getReference(bean, classToInstantiate, ctx);

            return dao;
        } catch (Exception e) {
            logger.error("Unable to construct {}. Creating the object directly.", classToInstantiate.getName());
            return classToInstantiate.newInstance();
        } finally {
            if (cc != null) cc.release();
        }
    }

    @Override
    public AtmosphereObjectFactory allowInjectionOf(Object o) {
        return this;
    }


    public String toString() {
        return "CDI ObjectFactory";
    }

    @Override
    public void configure(AtmosphereConfig config) {
        try {
            AtmosphereProducers p = newClassInstance(AtmosphereProducers.class,AtmosphereProducers.class);
            p.configure(config);
        } catch (Exception e) {
            logger.error("", e);
        }
    }
}

You can install it by adding in your pom.xml:

 <dependency>
    <groupId>org.atmosphere</groupId>
    <artifactId>atmosphere-cdi</artifactId>
    <version>2.3.0+</version>
</dependency>

Atmosphere will auto-detect it and use it automatically.

Using Spring

For Spring, Atmosphere will use the following code for injecting/creating object:

public class SpringWebObjectFactory implements AtmosphereObjectFactory<Class<?>> {
    /**
     * A comma delimited list of {@link org.atmosphere.inject.InjectableObjectFactory.DEFAULT_ATMOSPHERE_INJECTABLE} that
     * won't be created by Spring.
     */
    public static final String ATMOSPHERE_SPRING_EXCLUDE_CLASSES = "org.atmosphere.spring.excludedClasses";

    private static final Logger logger = LoggerFactory.getLogger(SpringWebObjectFactory.class);
    protected boolean preventSpringInjection = false;
    private final List<Class<?>> excludedFromInjection = new ArrayList<Class<?>>();

    private AnnotationConfigApplicationContext context;

    @Override
    public <T, U extends T> U newClassInstance(Class<T> classType,
                                               Class<U> classToInstantiate)
            throws InstantiationException, IllegalAccessException {

        if (preventSpringInjection && excludedFromInjection.contains(classType)) {
            logger.trace("Excluded from injection {}", classToInstantiate.getName());
            return classToInstantiate.newInstance();
        }

        String name = classToInstantiate.getSimpleName();
        if (!context.containsBeanDefinition(Introspector.decapitalize(name))) {
            context.register(classToInstantiate);
        }
        U t = context.getAutowireCapableBeanFactory().createBean(classToInstantiate);

        if (t == null) {
            logger.info("Unable to find {}. Creating the object directly."
                    + classToInstantiate.getName());
            return classToInstantiate.newInstance();
        }
        return t;
    }

    @Override
    public AtmosphereObjectFactory allowInjectionOf(Class<?> aClass) {
        context.register(aClass);
        return this;
    }

    public String toString() {
        return "Spring Web ObjectFactory";
    }

    @Override
    public void configure(AtmosphereConfig config) {
        try {

            String s = config.getInitParameter(ATMOSPHERE_SPRING_EXCLUDE_CLASSES);
            if (s != null) {
                String[] list = s.split(",");
                for (String clazz : list) {
                    excludedFromInjection.add(IOUtils.loadClass(getClass(), clazz));
                }

                if (list.length > 0) {
                    preventSpringInjection = true;
                }
            }

            context = new AnnotationConfigApplicationContext();
            context.setParent(WebApplicationContextUtils.getWebApplicationContext(config.framework().getServletContext()));

            context.refresh();

            // Hack to make it injectable
            context.register(AtmosphereConfig.class);
            context.getBean(AtmosphereConfig.class, config.framework()).populate(config);
        } catch (Exception ex) {
            logger.warn("Unable to configure injection", ex);
        }
    }
}

You can install it by adding in your pom.xml:

 <dependency>
    <groupId>org.atmosphere</groupId>
    <artifactId>atmosphere-spring</artifactId>
    <version>2.3.0+</version>
</dependency>

Using Guice

For Guice, Atmosphere will use the following code for injecting/creating object:

public class GuiceObjectFactory implements AtmosphereObjectFactory<AbstractModule> {

    private static final Logger logger = LoggerFactory.getLogger(GuiceObjectFactory.class);

    protected Injector injector;
    protected AtmosphereConfig config;
    private final List<AbstractModule> modules = new ArrayList<AbstractModule>();

    @Override
    public void configure(AtmosphereConfig config) {
        if (config == null) {
            throw new NullPointerException("AtmosphereConfig can't be null");
        }
        this.config = config;

        modules.add(new AtmosphereModule());

        try {
            AtmosphereProducers p = newClassInstance(AtmosphereProducers.class, AtmosphereProducers.class);
            p.configure(config);
        } catch (Exception e) {
            logger.error("", e);
        }
    }

    @Override
    public <T, U extends T> U newClassInstance(Class<T> classType, Class<U> classToInstantiate) throws InstantiationException, IllegalAccessException {
        initInjector(config.framework());
        U t;
        if (injector != null) {
            t = injector.getInstance(classToInstantiate);
        } else {
            logger.warn("No Guice Injector found in current ServletContext. Are you using {}?", AtmosphereGuiceServlet.class.getName());
            logger.trace("Unable to find {}. Creating the object directly.", classToInstantiate.getName());
            t = classToInstantiate.newInstance();
        }

        return t;
    }

    @Override
    public String toString() {
        return "Guice ObjectFactory";
    }

    protected void initInjector(AtmosphereFramework framework) {
        if (injector == null) {
            // start by trying to get an Injector instance from the servlet context
            Injector existingInjector = (Injector) framework.getServletContext().getAttribute(Injector.class.getName());

            AbstractModule[] a = modules.toArray(new AbstractModule[modules.size()]);
            if (existingInjector != null) {
                logger.trace("Adding AtmosphereModule to existing Guice injector");
                injector = existingInjector.createChildInjector(a);
            } else {
                logger.trace("Creating the Guice injector manually with AtmosphereModule");
                injector = Guice.createInjector(a);
            }
        }
    }

    @Override
    public AtmosphereObjectFactory allowInjectionOf(AbstractModule module) {
        modules.add(module);
        return this;
    }

    private class AtmosphereModule extends AbstractModule {
        @Override
        protected void configure() {
            bind(BroadcasterFactory.class).toProvider(new Provider<BroadcasterFactory>() {
                @Override
                public BroadcasterFactory get() {
                    return config.getBroadcasterFactory();
                }
            });
            bind(AtmosphereFramework.class).toProvider(new Provider<AtmosphereFramework>() {
                @Override
                public AtmosphereFramework get() {
                    return config.framework();
                }
            });
            bind(AtmosphereResourceFactory.class).toProvider(new Provider<AtmosphereResourceFactory>() {
                @Override
                public AtmosphereResourceFactory get() {
                    return config.resourcesFactory();
                }
            });
            bind(MetaBroadcaster.class).toProvider(new Provider<MetaBroadcaster>() {
                @Override
                public MetaBroadcaster get() {
                    return config.metaBroadcaster();
                }
            });
            bind(AtmosphereResourceSessionFactory.class).toProvider(new Provider<AtmosphereResourceSessionFactory>() {
                @Override
                public AtmosphereResourceSessionFactory get() {
                    return config.sessionFactory();
                }
            });
            bind(AtmosphereConfig.class).toProvider(new Provider<AtmosphereConfig>() {
                @Override
                public AtmosphereConfig get() {
                    return config;
                }
            });
        }
    }
}

You can install it by adding in your pom.xml:

 <dependency>
    <groupId>org.atmosphere</groupId>
    <artifactId>atmosphere-guice</artifactId>
    <version>2.3.0+</version>
</dependency>