I have uncovered some strange behavior trying to set a value with a view parameter. The code below does NOT work...upon submission of the form with the view parameter, the id value is NOT set. However, if the comments are removed from the getId and setId methods, the id value IS properly set. Is there some issue with JSF EL when accessing generics, or am I doing something terribly wrong? Can anyone explain this behavior?
Interestingly, I receive no exceptions...the value is simply not set. In fact, the setId method in the abstract class is not even called.
public abstract class AbstractBusinessObjectAction<E, ID extends Serializable> {
protected abstract BusinessObjectManager<E, ID> getBusinessObjectManager();
public ID getId() {
ID id = getBusinessObjectManager().getId();
return id;
}
public void setId(final ID id) {
getBusinessObjectManager().setId(id);
}
}
#Named("configuration")
#ConversationScoped
public class ConfigurationAction extends AbstractBusinessObjectAction<ConfigurationParameter, Long> {
#Inject
private ConfigurationParameterManager manager;
protected BusinessObjectManager<ConfigurationParameter, Long> getBusinessObjectManager() {
return manager;
}
// public Long getId() {
// Long id = super.getId();
// return id;
// }
// public void setId(Long id) {
// super.setId(id);
// }
}
Following is an except from the .xhtml:
<f:metadata>
<f:viewParam id="entityIdParam" name="entityId" value="#{configuration.id}" />
</f:metadata>
Adding a converter solved the problem.
<f:viewParam id="entityIdParam" name="entityId" value="#{configuration.id}" >
<f:converter converterId="javax.faces.Long" />
</f:viewParam>
To the best of my knowledge, this is the reason:
Because the id property is of type ID extends Serializable in the abstract class, JSF tries to install a non-existant Serializable converter. The reason the code works when I uncomment the methods indicated, is that JSF properly detects that it needs a javax.faces.Long converter based on the explicit declaration of Long on the property methods for the "id" property.
Thanks to #Damian for the tip!
Related
I searched similar questions but I'm a bit confused. I have a login page, so LoginBean also which is;
#ManagedBean(name = "loginBean")
#SessionScoped
public class LoginBean implements Serializable {
private String password="";
private String image="";
#ManagedProperty(value = "#{loginBeanIdentityNr}")
private String identityNr="";
...
after success, navigates to orderlist page, so I have also OrderBean.
#ManagedBean(name = "OrderBean")
#SessionScoped
public class OrderBean {
List<Ordery> sdList;
public List<Order> getSdList() {
try {
String identityNr ="";
ELContext elContext = FacesContext.getCurrentInstance().getELContext();
LoginBean lBean = (LoginBean) FacesContext.getCurrentInstance().getApplication().getELResolver().getValue(elContext, null, "loginBean");
identityNr =lBean.getIdentityNr();
sdList = DatabaseUtil.getOrderByIdentityNr(identityNr);
...
}
I don't need the whole LoginBean, just ManagedProperty "loginBeanIdentityNr". But this code below doesn't work (of course);
identityNr = (String) FacesContext.getCurrentInstance()
.getApplication().getELResolver()
.getValue(elContext, null, "loginBeanIdentityNr");
this time it returns null to me.
I think if I need whole bean property, I can inject these beans, right? So, do you have any suggestions for this approach? can<f:attribute> be used?
The #ManagedProperty declares the location where JSF should set the property, not where JSF should "export" the property. You need to just inject the LoginBean as property of OrderBean.
public class OrderBean {
#ManagedProperty(value="#{loginBean}")
private LoginBean loginBean; // +setter
// ...
}
This way you can access it in the OrderBean by just
loginBean.getIdentityNr();
Alternatively, if you make your OrderBean request or view scoped, then you can also set only the identityNr property.
public class OrderBean {
#ManagedProperty(value="#{loginBean.identityNr}")
private String identityNr; // +setter
// ...
}
Unrelated to the concrete problem: initializing String properties with an empty string is a poor practice.
I try to set the values of a selectManyCheckbox to my testBean backing bean.
If I use a property of type List<String> instead of Attributed<List<String>> it works perfectly. It is the workaround I'm using currently.
But on my backing bean I have a generic object which contains the List.
The javax.el.BeanELResolver resolve this to an Object. Which is correct due to Type erasure.
I tried to implement a custom ElResolver. But I should know to which type to convert the object to. It isn't obviously always a List. I have the information in the xhtml pages. So I hoped I could pass some child element which would contain the information, but could not find a way to access the child element from the ElResolver.
A Custom converted does not work either as it converts selectItems, not the List.
Here is the simplest form
<h:form>
<p:selectManyCheckbox value="#{testBean.attributed.value}" >
<f:selectItems value="#{testBean.selection}" />
</p:selectManyCheckbox>
<p:commandButton action="#{testBean.execute}" value="do it" />
</h:form>
and the bean
private Attributed<List<String>> attributed = new Attributed<>();
public Map<String, String> getSelection() {
return ImmutableMap.<String, String> of("key1", "value1", "key2", "value2");
}
public static class Attributed<T> {
private T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
public Attributed<List<String>> getAttributed() {
return attributed;
}
public void setAttributed(Attributed<List<String>> attributed) {
this.attributed = attributed;
}
So the question is:
Is there a way to set the value to testBean.attributed.value directly and with the correct type.
Is it possible by defining a custom ElResolver, or are there other way to do it?
I want to pass some IDs in a URL that are later going to be read by a backing bean. For each ID a particular operation will follow.
F.g. URL: localhost:8585/monit/accepted.jsf?acceptMsg=948&acceptMsg=764
The bean:
#ManagedBean
#RequestScoped
public class AcceptedBean {
#EJB
private MessageService messageService;
#ManagedProperty("#{paramValues.acceptMsg}")
private String[] acceptMsg;
public String[] getAcceptMsg() {
return acceptMsg;
}
public List<Message> getMessages() {
return messageService.getAcceptedMessages();
}
public void setAcceptMsg(String[] acceptMsgs) {
this.acceptMsg = acceptMsgs;
if(acceptMsg != null) {
try {
for (String accept: acceptMsg) {
final Message o = messageService.find(Integer.parseInt(accept));
messageService.accept(o);
FacesContext.getCurrentInstance().
addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_INFO,
"Message was accepted:", o.getSubject()));
}
} catch (Exception e) {
FacesContext.getCurrentInstance().
addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_ERROR,
"Error:", e.getMessage()));
}
}
}
}
acceped.xhtml:
....
<f:metadata>
<f:viewParam name="acceptMsg" value="#{acceptedBean.acceptMsg}" />
</f:metadata>
....
When I tried replacing value with binding in the .xhtml I got
Cannot convert javax.faces.component.UIViewParameter#ee2549 of type class javax.faces.component.UIViewParameter to class [Ljava.lang.String;
Any ideas on how to solve the conversion error would be appreciated!
The <f:viewParam> doesn't support multiple parameter values. Remove it. You don't need it in this particular case. You already have a #ManagedProperty("#{paramValues.acceptMsg}") which already sets them.
See also:
f:viewParam with multiple values
Unrelated to the concrete problem, I'd only replace the business job in the setter by a #PostConstruct method. The getters/setters should in principle not be doing anything else than getting or setting a property, that would indicate bad design as they are merely access points and can be invoked multiple times on a per-request basis.
<p:editor value="#{editorBean.value}" widgetVar="editor" width="686"
height="390" language="en" align="center">
</p:editor>
Following is my rich-text editor bean picked up from primefaces
#ManagedBean(name = "editorBean")
#SessionScoped
public class EditorBean {
private static final String MANAGED_BEAN_NAME = "editorBean";
private String value;
public static EditorBean getCurrentInstance() {
return (EditorBean) FacesContext.getCurrentInstance()
.getExternalContext().getRequestMap().get(MANAGED_BEAN_NAME);
}
public void setValue(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
Apart from this I have another bean say A. I have a method inside A that populates a HTML table. What I want is when the user opens the editor, it should be pre-populated with that HTML table data and of course the changes should get reflected into (String: value). Therefore, you can say that I am trying to tie up both the values together. I think it needs to be done with DI but somehow its not working. If someone can guide or quote an example, it would be really helpful.
One way to do it is rewrite your getValue() method to pick up the value from bean A.
And yes, the reference to your A bean should come from DI:
//injecting a reference to A
#ManagedPropery(value="#{A}") //or whatever is the name of your bean
private A beanA;
public void setBeanA(A beanA) {
this.beanA = beanA;
}
Or, with CDI, just:
#Inject private A beanA
Finally, your getValue method
public String getValue() {
return beanA.getValue()
}
I have a custom user details object with first name part of it. Below username works, but I want something like the second to work. How can I access this custom property?
<security:authentication property="principal.username" />
<security:authentication property="principal.firstname" />
I presume that you tried the above and that it didn't work.
Check your custom user details class to make sure that the capitalization of the getter and setter methods for the 'firstname' property are correct.
Works for me. Here's my test code:-
CustomUserDetails class
public class CustomUserDetails implements UserDetails {
public String getFirstName() {
return "hello";
}
...
}
Custom tag in JSP
The following tag returns hello.
<security:authentication property="principal.firstName" />
By the way, make sure you are not putting getFirstName() into the anonymous class, because that will not work.
What I'm trying to say here is, don't do this:-
...
return new UserDetails() {
// adding extra method here will not work
public String getFirstName() {
return "hello";
}
public String getUsername() {
return "test";
}
...
};
... do this:-
...
// this class implements UserDetails and contains getFirstName()
CustomUserDetails csd = new CustomUserDetails();
csd.set...(...)
...
return csd;