Since #ManagedBean(eager = true) will be deprecated, How do you create a eager application scoped cdi bean?
Solved this by making use of CDI extension framework:
Create Qualifier:
#Qualifier
#Target(value = {ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
#Retention(value = RetentionPolicy.RUNTIME)
public #interface Eager {
}
Create Extension implementation with observer method:
public class EagerCDIExtension implements Extension {
public void afterDeploymentValidation(#Observes AfterDeploymentValidation event, BeanManager beanManager) {
beanManager.getBeans(Object.class, new AnnotationLiteral<Eager>() {
}).parallelStream().filter(bean -> bean.getBeanClass().isAnnotationPresent(ApplicationScoped.class)).forEach(bean -> {
beanManager.getReference(bean, bean.getBeanClass(), beanManager.createCreationalContext(bean)).toString();
});
}
}
Create: META-INF/services/javax.enterprise.inject.spi.Extension
Register extension implementatoin class in above file i.e:
pypackage.EagerCDIExtension
Related
In a JSF project, we wrote our own PartialViewContext to listen to some events fired by pages beans:
#RequestScoped
public class OurPartialViewContext extends PartialViewContextWrapper
{
...
// called by cdi
#SuppressWarnings("unused")
private void listenForUpdate(#Observes OurRefreshEvent event)
{
...
And we wrote the matching factory, injecting it:
public class OurPartialViewContextFactory extends PartialViewContextFactory
{
#Inject
private OurPartialViewContext customPartialViewContext;
...
Problem is that in the newest versions of JSF, the empty constructor for PartialViewContextWrapper is deprecated, asking us to use another constructor with the wrapped object in parameter.
Currently, our PartialViewContext needs to be tied to the request scope, in order to be modified during the request by the observed events and to be used by a custom PartialResponseWriter we also wrote.
So our PartialViewContext currently both:
must have an empty constructor, as it is a #RequestScoped bean;
should not have an empty constructor, as it is deprecated for PartialViewContextWrapper which it inherits from.
How could we find a solution there?
We tried to remove it from the scope and build it in the Factory with a simple new OurPartialViewContext(), but then the #Observes methods are never called.
You are required to pass the wrapped instance into the constructor and to use getWrapped() over all place in delegate methods. Otherwise your application will most probably not work when you install other JSF libraries which also ship with their own PartialViewContext implementation such as OmniFaces and PrimeFaces. You would be effectively completely skipping the functionality of their PartialViewContext implementation. This mistake was previously observed in too many custom implementations of factory-provided classes. Hence the urge to mark the default constructor as #Deprecated so that the developers are forced to use the proper design pattern.
Your specific issue can be solved by simply refactoring the listenForUpdate() method into a fullworthy request scoped CDI bean, which you then inject in the factory who in turn ultimately passes it into the constructor of your PartialViewContext implementation.
Thus, so:
#RequestScoped
public class OurEventObserver {
public void listenForUpdate(#Observes OurRefreshEvent event) {
// ...
}
}
public class OurPartialViewContextFactory extends PartialViewContextFactory {
#Inject
private OurEventObserver observer;
public OurPartialViewContextFactory(PartialViewContextFactory wrapped) {
super(wrapped);
}
#Override
public PartialViewContext getPartialViewContext(FacesContext context) {
PartialViewContext wrapped = getWrapped().getPartialViewContext(context);
return new OurPartialViewContext(wrapped, observer);
}
}
public class OurPartialViewContext extends PartialViewContextWrapper {
private OurEventObserver observer;
public OurPartialViewContext(PartialViewContext wrapped, OurEventObserver observer) {
super(wrapped);
this.observer = observer;
}
// ...
}
Inside any of the overridden methods of OurPartialViewContext you can simply access the state of the observer, provided that the listenForUpdate() modifies some instance variables representing the state.
While running a unit test with Arquillian using Glassfish embedded plugin, I get the following CDI error :
2015-09-18 06:25:24,376 DEBUG | main | org.jboss.weld.Reflection | WELD-000620: interface com.SupportedLocales is not declared #Target(METHOD, FIELD, PARAMETER, TYPE). Weld will use this annotation, however this may make the application unportable.
sept. 18, 2015 6:25:24 AM com.sun.enterprise.v3.server.ApplicationLifecycle deploy
GRAVE: Exception during lifecycle processing
org.glassfish.deployment.common.DeploymentException: CDI deployment failure:WELD-001408: Unsatisfied dependencies for type Set<Locale> with qualifiers #SupportedLocales
at injection point [BackedAnnotatedParameter] Parameter 1 of [BackedAnnotatedConstructor] #Inject protected com.MyClass(#SupportedLocales Set<Locale>)
at com.MyClass.<init>(MyClass.java:0)
Set(Locale) with qualifier #SupportedLocales is defined in a module deployed in the tested WebArchive. The Archive content is :
/WEB-INF/
/WEB-INF/lib/
/WEB-INF/lib/commons-lang3-3.3.2.jar
/WEB-INF/lib/commons-configuration-1.10.jar
/WEB-INF/lib/reflections-0.9.9-RC2.jar
/WEB-INF/lib/jcl-over-slf4j-1.7.10.jar
/WEB-INF/lib/slf4j-api-1.7.10.jar
/WEB-INF/lib/deltaspike-core-api-1.5.0.jar
/WEB-INF/lib/commons-util-1.0.0-SNAPSHOT.jar
/WEB-INF/lib/commons-io-2.4.jar
/WEB-INF/lib/guava-16.0.1.jar
/WEB-INF/lib/log4j-over-slf4j-1.7.10.jar
/WEB-INF/lib/javassist-3.18.2-GA.jar
/WEB-INF/lib/logback-classic-1.1.2.jar
/WEB-INF/lib/logback-core-1.1.2.jar
/WEB-INF/lib/jul-to-slf4j-1.7.10.jar
/WEB-INF/lib/commons-cdi-1.0.0-SNAPSHOT.jar
/WEB-INF/lib/xml-apis-1.0.b2.jar
/WEB-INF/lib/deltaspike-core-impl-1.5.0.jar
/WEB-INF/lib/dom4j-1.6.1.jar
/WEB-INF/lib/commons-codec-1.9.jar
/WEB-INF/lib/commons-lang-2.6.jar
/WEB-INF/lib/annotations-2.0.1.jar
/WEB-INF/lib/libphonenumber-7.0.3.jar
/WEB-INF/classes/
/WEB-INF/classes/com/
/WEB-INF/classes/com/timm/
/WEB-INF/classes/com/timm/common/
/WEB-INF/classes/com/timm/common/cdi/
/WEB-INF/classes/com/timm/common/cdi/web/
/WEB-INF/classes/com/timm/common/cdi/web/i18n/
/WEB-INF/classes/com/timm/common/cdi/web/i18n/ShiroCurrentLocale.class
/WEB-INF/beans.xml
This object is provided by a producer method located in "common-cdi" module. The same module provide CDI extension feature like ThreadScoped. This producer is not discovered by Weld during test startup and Weld does not discover beans from "commons-cdi" module. How is it possible? Can we provide CDI extension features and CDI bean in the same module ?
#SupportedLocales is declares in "commons-cdi" with:
#Qualifier
#Target({
ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD
})
#Retention(RetentionPolicy.RUNTIME)
public #interface SupportedLocales {
}
The producer is declared in "commons-cdi" with:
#Dependent
public class I18NProducer {
#Produces
#ApplicationScoped
#Default
#SupportedLocales
public Set<Locale> getSupportedLocales() {
Set<Locale> supportedLocales;
supportedLocales = new HashSet<Locale>();
supportedLocales.add(Locale.US);
return supportedLocales;
}
}
JUnit test case definition:
#RunWith(Arquillian.class)
public class LocaleInjectionTest {
#Deployment
public static Archive<?> deploy() {
final String moduleName = LocaleInjectionTest.class.getSimpleName();
PomEquippedResolveStage resolver = Maven.resolver().loadPomFromFile("pom.xml");
File[] libs = resolver.resolve("com.myname:commons-cdi").withTransitivity().asFile();
WebArchive testWar = ShrinkWrap
.create(WebArchive.class, moduleName + ".war")
.addClass(MyCurrentLocale.class)
.addAsLibraries(libs)
.addAsWebInfResource(ArchiveUtils.getResourceBeanXMLWithAlternativeAndDiscoveryModeAnnotated(MyCurrentLocale.class),
"beans.xml");
return testWar;
}
#Inject
private CurrentLocale bean;
#Test
public void testInjection() throws Exception {
...
}
}
MyCurrentLocale class definition:
#SessionScoped
#Alternative
public class MyCurrentLocale extends DefaultCurrentLocale implements Serializable {
#Inject
protected MyCurrentLocale(#SupportedLocales Set<Locale> supportedLocales) {
super(supportedLocales);
}
...
}
Is there something wrong in declaration ?
Looks like you're using GlassFish v3, so you'll need the beans.xml file in the jar as well as it is a bean archive too.
I am using TomEE+ 1.7.1.
With JSF managed beans this code was working well:
#ManagedBean( eager = true )
#ApplicationScoped
public class AppBean {
#PostConstruct
public void init() {
ServletContext sc = (ServletContext) FacesContext.getCurrentInstance().getExternalContext().getContext();
if (GlobalSettings.TESTMODE) {
sc.getSessionCookieConfig().setDomain("." + GlobalSettings.APP_DOMAIN_TEST);
} else {
sc.getSessionCookieConfig().setDomain("." + GlobalSettings.APP_DOMAIN);
}
}
}
The init function ran at application startup and ServletContext was available.
I read everywhere that it's time to migrate to CDI beans instead of JSF beans. So I wanted to change #ManagedBean( eager = true ) to #Named #Eager (#Eager is from Omnifaces). Init function is running at application startup, but there is no FacesContext so I can't get ServletContext.
General question: How to get ServletContext in a non-request environment in CDI beans? (ServletContext is not a 'per request' object, so it should exist before the first request.)
Specific question: how to set the domain for the session cookies dynamically from code but before the first request occurs?
You should be using a ServletContextListener for the purpose of performing programmatic configuration on a servlet based application.
#WebListener
public class Config implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent event) {
ServletContext servletContext = event.getServletContext();
// ...
}
#Override
public void contextDestroyed(ServletContextEvent event) {
ServletContext servletContext = event.getServletContext();
// ...
}
}
A #WebListener is inherently also CDI managed and thus you can just use #Inject and friends in there.
An application scoped managed bean is intented for holding application scoped data/state which can be used/shared across requests/views/sessions.
Per the CDI spec, you can #Inject the ServletContext into a CDI bean. Just be sure to do it in a #PostConstruct, as injected fields are available only after construction:
#Inject ServletContext extCtxt;
#PostConstruct
public void doSomething(){
// do something with your injected field
}
I have some legacy code that put objects as http session attributes using code like this:
MyObject object = new MyObject();
Map<String, Object> sessionMap = FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
sessionMap.put("attrname", object);
The old facelets accessed the code using
#ManagedProperty("#{attrname}")
private MyObject object;
Is there any way using CDI (#Inject) to inject this session attribute to a Bean?
In new code that uses CDI what's the better way to create and inject objects that need to be created in a controlled way.
Get hold of it in a session scoped managed bean with a #Produces#Named on the getter.
#SessionScoped
public class MyObjectProducer implements Serializable {
private MyObject myObject;
#Produces
#Named("attrname")
public MyObject getMyObject() {
return myObject;
}
public void setMyObject(MyObject myObject) {
this.myObject = myObject;
}
}
When you set it somehow via e.g. myObjectProducer.setMyObject(myObject) elsewhere (or perhaps a CDI #Observes event), then you can inject it anywhere using #Inject #Named.
#Inject
#Named("attrname")
private MyObject myObject;
And yes, it's still available via #{attrname} in EL the usual way. And no, it won't be auto-created when not set, it'll remain null until you actually set it as a property of the producer class.
Alternatively, if you really intend to keep the legacy way of setting the instance via ExternalContext#getSessionMap() (e.g. because it's third party and you can thus not change it), then you can alternatively also let the producer return it directly from the session map:
#SessionScoped
public class MyObjectProducer implements Serializable {
#Produces
#Named("attrname")
public MyObject getMyObject() {
return (MyObject) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("attrname");
}
}
This however isn't guaranteed to work when injected in a non-JSF artifact, such as an arbitrary #WebServlet, as the FacesContext#getCurrentInstance() would obviously return null.
This is the way to go, for both non-jsf and jsf artifacts.
#Qualifier
#Retention(RUNTIME)
#Target({TYPE,METHOD,FIELD,PARAMETER});
public #interface SessionAttribute {
#NonBinding
String value() default "";
}
#ApplicationScope
public class SessionAttributeService {
//Dont worry, this is a proxy, and CDI will ensure that the right one called at the right time.
#Inject
private HttpServletRequest servletRequest;
#Produces
#SessionAttribute
#RequestScope
public String sessionAttribute(final InjectionPoint ip){
final SessionAttribute sa = ip.getAnnotated().getAnnotation(SessionAttribute.class);
final HttpSession session = servletRequest.getSession();
return session.getAttribute(sa.value());
}
}
And use case:
#RequestScope
public class MyServiceBean {
#Inject
#SessionAttribute("theAttribute")
private String attribute;
}
I'm writing Java SE application that uses CDI.
I have bean definition:
public class BeanA {
#PostConstruct
public void init() {
System.out.println("INIT");
}
public void receive(#Observes String test) {
System.out.println("received: " + test);
}
}
Requirements:
- I need to have many instances of BeanA in application
- I'd like to use Event CDI mechanism to communicate with that objects
When I use #Dependent scope, then #PostConstruct of BeanA is called everytime a new message is received. When I use #Singleton or #ApplicationScope then I can't have many objects of BeanA type.
What is solution to my problem?