Based on the third option/example provided in this answer https://stackoverflow.com/a/7531284/944396 I tried to have a converter be a property in my ViewScoped bean; however, when I do so it breaks the view scope for that bean (it gets constructed multiple times when it should not be).
I am not sure if I am doing something wrong, or if that example is assuming you are running a newer version of Mojarra that has fixed the ViewScope/binding issue. I myself am running Mojarra 2.1.6. Here is the bean code:
#ManagedBean
#ViewScoped
public class Bean {
private Double myVal;
private Converter converter;
public void Bean() {
converter = new MyCustomConverterClass();
}
public Converter getConverter() {
return converter;
}
public Double getMyVal() {
return myVal;
}
public void setMyVal(Double myVal) {
this.myVal = myVal;
}
}
And the usage:
<h:outputText value="#{bean.myVal}" converter="#{bean.converter}" />
You're right, it breaks the view scope. The converter attribute is evaluated during view build time. Apart from using other options listed in the answer you found How to set converter properties for each row of a datatable?, you'd need to upgrade to at least Mojarra 2.1.18.
Related
In my JSF application I'm using a #ViewScoped bean Publication to show/edit data coming from my database. In that bean there is a field for a subtype-specific data object, i.e. containing a different object depending on whether the publication is, say, a book or an article.
#ViewScoped
#Named
public class Publication implements Serializable {
#Inject
DatabaseStorage storage;
...
String id;
String type;
PublicationType typedStuff;
#PostConstruct
public void init() {
// Get an URL parameter from the request,
// look up row in database accordingly, initialize String "type".
switch (type) {
case "ARTICLE":
typedStuff = new Article(id);
break;
case "BOOK":
typedStuff = new Book(id);
break;
default:
break;
}
}
}
...with classes Article and Book that implement / extend PublicationType.
So far, so good, but I would like for typedStuff to be a CDI bean, so that I can inject useful resources there.
I've read this and this page on producer methods, as well as this tutorial and this very related SO question, but none of them answer precisely my question: Can I inject based on a field that the injecting bean itself only knows at runtime?
I've gotten the producer method to work as such, but I can't parametrize it, so I can't get that switch to work.
If I put the producer method in a separate class (or bean) then I don't have access to the type field.
If I inject the injecting bean into the producer class, or move the producer method into the injecting class, I get a circular injection.
If I put the producer method statically into the injecting class, I also don't have access, because type cannot be static. (Although, since it's only used momentarily...?)
Also (and that is probably the answer right there), the producer method is executed before my injecting bean's init method, so type wouldn't even have been set yet.
Does anybody have a better idea?
No you cannot, but you can select a bean based on the field value. Say:
public interface PublicationType {}
#PType("ARTICLE")
public class Article implements PublicationType{}
#PType("BOOK")
public class Book implements PublicationType {}
And define a qualifier:
public #interface PType {
String value();
}
And define an AnnotationLiteral:
public class PTypeLiteral extends AnnotationLiteral<PType> implements PType {}
Then you can use:
public class Publication {
#Any
#Inject
private Instance<PublicationType> publicationTypes;
public void doSomething() {
PType ptype = new PTypeLiteral(type);
// Of course you will have to handle all the kind of exceptions here.
PublicationType publicationType = publicationTypes.select(ptype).get();
}
}
There is the javax.inject.Provider interface (I think You are using #Named and #Inject annotations from the same package).
You could use it to achieve what You want. It will create instances for You with injected fields.
One drawback is that You will have to set the id yourself.
#ViewScoped
#Named
public class Publication implements Serializable {
#Inject
DatabaseStorage storage;
#Inject
Provider<Article> articleProvider;
#Inject
Provider<Book> bookProvider;
String id;
String type;
PublicationType typedStuff;
#PostConstruct
public void init() {
// Get an URL parameter from the request,
// look up row in database accordingly, initialize String "type".
switch (type) {
case "ARTICLE":
typedStuff = articleProvider.get();
typedStuff.setId(id);
break;
case "BOOK":
typedStuff = bookProvider.get();
typedStuff.setId(id);
break;
default:
break;
}
}
}
I am using jsf2.2 with wildfly 8.1 and javaee7.
My CDI bean injection in the phaselistener works as expected, but the #PostConstuct method is never invocked
I have tried to annotate the phaselistener with #javax.enterprise.context.ApplicationScope, SessionScope and Dependent to no avail.
Apart from naming, this is the exact thing i do in my post construct.
//#ApplicationScope
//#SessionScope
//#Dependent
public class MyPhaseListener implements PhaseListener {
#Inject
#Any
private Instance<MyOrderedUrlHandler> myOrderedUrlhandlers;
private Map<String, List<MyOrderedUrlHandler> orderedUrlHandlersMap;
#PostConstruct
void mapOrderedUrlHandlers() {
LOG.info("Executing postconstruct");
orderedUrlHandlersMap = Maps.newHashMap();
for(final MyOrderedUrlHandler urlhandler : myOrderedUrlhandlers) {
final String handles = urlhandler.url();
final List<MyOrderedUrlHandler> registeredHandlers = orderedUrlHandlersMap.get(handles);
if(registeredHandlers == null) {
registeredHandlers = Lists.newArraList();
}
registeredHandlers.add(urlHandler);
orderedUrlHandlersMap.put(handles, registeredHandlers);
}
}
}
Method level injection also works fine.
Is it the case that #PostConstruct callback is not part of jsf phaselistener specs?
According to section 5.4.1 of the JSF 2.2 spec, PhaseListener is not a managed bean but is injectable.
According to section 5.4.2, managed beans must support lifecycle annotations #PostConstruct and #PreDestroy.
Since a PhaseListener is not a managed bean in the sense of JSF, it does not follow from the spec that a phase listener implementation must support #PostConstruct.
I have a Managed Bean:
public class CategoriaManagedBean {
#EJB
private CategoriaBeanLocal categoriaBean;
private Categoria categoria;
private List<Categoria> menu;
}
In my constructor I try:
public CategoriaManagedBean() {
menu = categoriaBean.findByIdCategoriaPadre(0);
}
But I get a error "Cannot create the instance of the class", why can't I initialize the attribute in the constructor?
I fix the problem with:
#PostConstruct
public void init() {
menu = categoriaBean.findByIdCategoriaPadre(0);
}
But I want to know the reason and if I am doing well with #PostConstruct
Greetings.
Using #PostConstruct is the correct approach.
EJBs are injected after the constructor is invoked on a ManagedBean.
That's why there is a #PostConstruct annotation.
Here's the first line from the documentation:
The PostConstruct annotation is used on a method that needs to be executed after dependency injection is done to perform any initialization.
I have two beans:
#Named
#SessionScoped
public class Session implements Serializable {
private String temp;
+getter, setter
}
#Named
#RequestScoped
public class Test {
#Inject
private Session s;
#PostConstruct
public void init() {
this.sth = s.getTemp(); //here is exception
}
}
When I try to fetch in init same value from session I get NullPointerException. What am I doing wrong? Server is JBoss eap 6.0, JSF v2.1. Thanks in advance
Edit: I investigated that problem appears only when I have more than 11 items in primefaces submenu component. If I have less than 11 items every thinks work OK. I am using PrimeFaces v 3.5.
Edit 2: On session bean only constructor is invoked. I have method annotated with PostConstruct but it is not called. I always get NullPointerException after invoking any method on session from Test bean.
I am using JSF 2.0 to build an app.
What i wish to do is depending on whether a checkbox is ticked or not by the user, i wish to change the scope of the bean from request to session .. is this possible ?
Or override the default 'session' scope with request scope when the checkbox is ticked ...
I tried researching a lot , but i am not even sure if this is possible.
Thanks.
It's not possible to change the scope of a managed bean during runtime. You can however easily change the behavior of a bean during runtime.
E.g.
#ManagedBean
#RequestScoped
public class MultiScopedBean {
#ManagedProperty("#{requestScopedBean}")
private RequestScopedBean requestScopedBean;
#ManagedProperty("#{sessionScopedBean}")
private SessionScopedBean sessionScopedBean;
private boolean sessionScoped; // Bind this to the checkbox.
// ...
public Object getSomeProperty() {
if (sessionScoped) {
return sessionScopedBean.getSomeProperty();
} else {
return requestScopedBean.getSomeProperty();
}
}
public void setSomeProperty(Object someProperty) {
if (sessionScoped) {
sessionScopedBean.setSomeProperty(someProperty);
} else {
requestScopedBean.setSomeProperty(someProperty);
}
}
// ...
}
Yes, it'll end up in quite some boilerplate, but that's what you get for such an odd requirement.