What is the equivalent of @ManagedBean(eager=true) in CDI
Asked Answered
P

4

14

As we all know that it is recommended to use annotations from javax.enterprise.context instead of javax.faces.bean as they are getting deprecated.

And we all found ManagedBeans with eager="true" annotated with @ApplicationScoped from javax.faces.bean and having a @PostConstruct method are very useful to do web application initialization e.g: read properties from file system, initialize database connections, etc...

Example :

import javax.faces.bean.ApplicationScoped;
import javax.faces.bean.ManagedBean;
import javax.annotation.PostConstruct;

@ApplicationScoped
@ManagedBean(eager=true)
public class someBean{

    @PostConstruct
    public void init(){
        //Do all needed application initialization.
    }
    ...
}

What I want to know is how can I get the same behavior if I used annotations from javax.enterprise.context.

Note: @Startup annotation from javax.ejb will help to run that code but only at the moment of deployment of the webapp when the application server Starts.

Promiscuity answered 16/7, 2016 at 14:46 Comment(0)
E
16

This is not provided by CDI or JSF. You could homegrow your own with a custom CDI qualifier and a ServletContextListener to hook on webapp start.

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Eager {
    //
}

@WebListener
public class EagerListener implements ServletContextListener{

    private static final AnnotationLiteral<Eager> EAGER_ANNOTATION = new AnnotationLiteral<Eager>() {
        private static final long serialVersionUID = 1L;
    };

    @Override
    public void contextInitialized(ServletContextEvent event) {
        CDI.current().select(EAGER_ANNOTATION).forEach(bean -> bean.toString());
    }

    @Override
    public void contextDestroyed(ServletContextEvent event) {
        // NOOP.
    }

}

(note: toString() triggers lazy instantiation)

import com.example.Eager;
import javax.enterprise.context.ApplicationScoped;

@Eager
@ApplicationScoped
public class YourEagerApplicationScopedBean {

    @PostConstruct
    public void init() {
        System.out.println("Application scoped init!");
    }
}

As to existing libraries, only JSF utility library OmniFaces offers @Eager out the box.

import org.omnifaces.cdi.Eager;
import javax.enterprise.context.ApplicationScoped;

@Eager
@ApplicationScoped
public class YourEagerApplicationScopedBean {

    @PostConstruct
    public void init() {
        System.out.println("Application scoped init!");
    }
}

It's also supported on @SessionScoped, @ViewScoped and @RequestScoped.

Regardless of the approach, the only disadvantage is that FacesContext isn't available at the moment the bean is constructed. But that shouldn't be a big problem, with CDI you can simply directly @Inject artifacts of interest such as ServletContext or HttpSession.

Electrothermal answered 16/7, 2016 at 15:20 Comment(1)
One problem here, the qualifier @Eager should be added to injection point. Based on your answer, I posted an answer below that solves the issue. Thanks.Tolley
H
2

CDI 1.1 also offers a standard way to observe scope lifecycle events, for instance:

public void processApplicationScopedInit(@Observes @Initialized(ApplicationScoped.class) ServletContext payload) {}
public void processApplicationScopedDestroyed(@Observes @Destroyed(ApplicationScoped.class) ServletContext payload) {}

For more information: http://www.next-presso.com/2014/06/you-think-you-know-everything-about-cdi-events-think-again/

Hamal answered 23/3, 2017 at 10:19 Comment(0)
P
2

As an alternative, you could use EJB instead of CDI. Then you can have a @Singleton with @Startup

import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
import javax.ejb.Startup;

@Singleton
@Startup
public class SomeBean {

    @PostConstruct
    public void init(){
        //Do all needed application initialization.
    }
    ...
}
Plague answered 11/4, 2018 at 13:26 Comment(2)
Why not use your proposed bean and inject the CDI beans that you need to be eagerly instatiated? Name the class EagerInstatiator.Dory
That would be just briedging. EagerInstatiator could directly be the name of this EJB. ALthough if you need a more complex orchestration you could do that, and use this EJB to orchestrate the different Beans that take part on the initializationPlague
T
0

Here is one approach I use:

import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Any;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.util.AnnotationLiteral;
import javax.inject.Inject;

@Startup
@Singleton
public class AppStartup
{
    @Inject
    private BeanManager beanManager;

    @PostConstruct
    public void init()
    {       
        // enforce initializing eager CDI beans
        var beans = beanManager.getBeans(Object.class, new AnnotationLiteral<Any>(){});
        for(var bean : beans)
        {
            if(bean.getBeanClass().getAnnotation(Eager.class) != null && bean.getBeanClass().getAnnotation(ApplicationScoped.class) != null)
            {
                var beanProxyInstance = beanManager.getReference(bean, bean.getBeanClass(), beanManager.createCreationalContext(bean));

                // invoking toString() on the proxy object will invoke the method annotated with @PostConstruct, if has not been invoked yet
                beanProxyInstance.toString();
            }
        }
    }
}
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Retention(RUNTIME)
@Target({TYPE})
public @interface Eager {}
import javax.enterprise.context.ApplicationScoped;

@Eager
@ApplicationScoped
public class SomeCdiBean {}

Now you can inject this CDI bean without any extra qualifiers:

@Inject
private SomeCdiBean someCdiBean;
Tolley answered 20/12, 2019 at 22:0 Comment(1)
And if one starts using ejb's there is a more simple solution, see one of the other answersCelebrate

© 2022 - 2024 — McMap. All rights reserved.