#FacesConverter without converter-id and forClass - jsf-2

What should work properly if I do not specify the converter-id and(or) forClass in determining the #FacesConverter annotation?
For example:
#FacesConverter
public class SplitConverter implements Converter{
#Override
public Object getAsObject(FacesContext context, UIComponent component,
String value) {
//...
}
#Override
public String getAsString(FacesContext context, UIComponent component,
Object value) {
//...
}
After registred in faces-config.xml and use that on .xhtml page:
<h:inputText id="badges" value="#{article.badges}"
required="true">
<f:converter converterId="com.katcgr.SplitConverter" />
</h:inputText>
And all is work. The documentation say that
if converter-id is the empty string, Application.addConverter(java.lang.Class,java.lang.String) is called, passing the converter-for-class as the first argument and the derived converter-class as the second argument.
But why everything works fine even if I not specified the forClass ?

After registred in faces-config.xml
Registration via #FacesConverter and <converter> in faces-config.xml are mutually exclusive whereby the XML registration overrides any annotation registration. So, when your converter is referenced via the converter ID as registered in XML, then the converter instance behaves basically exactly as if it had no annotations. If you remove the XML registration, then you should have retrieved the below exception on specified converterId:
javax.faces.FacesException: Expression Error: Named Object: com.katcgr.SplitConverter not found.
at com.sun.faces.application.ApplicationImpl.createConverter(ApplicationImpl.java:1339)
at javax.faces.application.ApplicationWrapper.createConverter(ApplicationWrapper.java:393)
at com.sun.faces.facelets.tag.jsf.ConverterTagHandlerDelegateImpl.createConverter(ConverterTagHandlerDelegateImpl.java:158)
...
This would basically only work if you had a
#FacesConverter("com.katcgr.SplitConverter")
If you however remove both the XML configuration and the <f:converter>, then it will "work" because the #FacesConverter without an explicit forClass will be automatically invoked for every bean property which is an instance of java.lang.Object (basically, everything), which does not have a more specific converter already registered. In other words, your converter will behave like:
#FacesConverter(forClass=Object.class)
It hopefully doesn't need an elaborate explanation that this is a terribly bad idea. It will also confuse PrimeFaces, because it will then internally initialize and use it as default converter for String class.

Related

Which one takes higher precendence between value and forClass in FacesConverter annotation when defining custom converter?

I have the following as annotation for the custom converter
#FacesConverter(value="capsConverter", forClass=String.class)
public class CapsConverter implements Converter{
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
return value.toUpperCase();
}
#Override
public String getAsString(FacesContext context, UIComponent component,Object value) {
if(value!=null && value instanceof String){
String s = (String)value;
s = WordUtils.capitalize(s.toLowerCase());
return s;
}
return null;
}
}
Issue is, the converter sometimes gets called even when I didnt explicitly call it in my page, does the forClass have anything to do with the interference for my inputTexts, should I be using value only and remove forClass=String.class ?
Any shed of light is highly appreciated.
It are actually two distinct ways of registering a converter. Both are mutually exclusive. They have no influence on each other and you can omit one or the other.
The forClass attribute will register the converter in the JSF application to kick in during processing and rendering of every model value of the specified type. In other words, with forClass=String.class, the converter will be implicitly invoked for every String typed input and output, unless the associated input/output component has already an explicit converter declared.
Generally you use forClass only for custom/complex types, not for standard types such as String, Long, BigDecimal, etc for which JSF has already builtin implicit converters.
So you should indeed remove it and rely on converter ID only.
#FacesConverter("capsConverter")
<h:someComponent ... converter="capsConverter" />
An example of correct appliance of forClass would be the following converter between a Person entity and a String representing its ID
#FacesConverter(forClass=Person.class)
on something like
<f:viewParam name="id" value="#{bean.person}" />
which converts an incoming request parameter representing an entity ID like below
/edit-person.xhtml?id=42
to a fullworthy entity property in bean (without needing any additional logic in bean)
private Person person;

How to pass the validator method to a composite component

I have my own inputText composite component created which wraps the p:inputText from Primefaces 3.4.5. It works well, but I can not figure out how to pass a bean validator method to the validator attribute in the p:inputText. I have read about using f:validator tags inside the composite component, but I really need to use the validator attribute on the p:inputText. The method and validation code works fine if I remove the composite component from my xhtml and just use a regular p:inputText.
My cc attribute is:
<composite:attribute name="validator" method-signature="void f(javax.faces.context.FacesContext,
javax.faces.component.UIComponent,
java.lang.Object)" targets="inputTextField"/>
my inputText inside the composite component is:
<p:inputText id="inputTextField"
... other attributes ...
validator="#{cc.attrs.validator}" />
when I use this composite component and pass a validator method to it, I get a property not found message...
Exception type: class javax.el.PropertyNotFoundException
Exception message: /view/configuration/dictionaryupdate/DictionaryItem.xhtml #27,123 validator="#{dictionaryUpdateController.validateCode}": The class 'com.ntst.caremanager.feature.dictionaryupdate.controllers.DictionaryUpdateController' does not have the property 'validateCode'.
However, I do have that method defined in my view-scoped controller bean:
public void validateCode(FacesContext context, UIComponent component, Object value) throws ValidatorException {
... code ....
}
Does anyone know what I am missing?
I am using Mojarra 2.1.21
Thank you

spring MVC model attributes in jsp page

When I access simple model data like the username using : ${username} everything seems to be fine. But when I try to use ${userdata.uname} I get an error like :
Could not find property uname in class com.mydom.datahandling.userdata
userdata is java class I add to the ModelAndView
ModelAndView mav = new ModelAndView("page37");
mav.addObject("sessionID",ID);
mav.addObject("userdata",p37userdata);
What do I need to do differently ? This seems to be happening whenever I try to use something other than a primitive type or String. I obviously need a bit more education. Do I need to serialize p37userdata?
userdata is
public class userdata{
public String uname;
public String otherstuff;
}
The objects you reference from your EL in your JSP should be java beans (or a map). According to wikipedia (the source of all truth) A bean has the following properties:
They are serializable, have a 0-argument constructor, and allow access
to properties using getter and setter methods.
So you should probably add getters and setters to your userdata class.

Use ManagedBean in FacesConverter

I want to use ManagedBean in my Converter. The ManagedBean is responsible for getting data from database. In Converter I want to convert string into object which must be get from database.
This is my Converter
#FacesConverter(forClass=Gallery.class, value="galleryConverter")
public class GalleryConverter implements Converter {
// of course this one is null
#ManagedProperty(value="#{galleryContainer}")
private GalleryContainer galleryContainer;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String galleryId) {
return galleryContainer.findGallery(galleryId);
...
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object gallery) {
...
}
}
I know that galleryContainer will be null and if I want to inject ManagedBean into Converter I can mark it as ManagedBean too. The problem is that I want to do it in beautiful way, I don't want to look for some 'strange solution'. Maybe the problem is in my application? Maybe there is some other good solution to create object which must get data from database and used in converter? I want also to mention that I will prefer to use DependencyInjection instead of creating new object using new statement (it is easier to test and maintain). Any suggestions?
Instead of using #FacesConverter you should use #ManagedBean, because currently faces converter isn't a valid injection target. Nonetheless, you can choose your converter to be a managed bean, thus refer to it in your view as converter="#{yourConverter}" (by managed bean name) instead of converter="yourConverter" (by converter id).
Basic usage example:
#ManagedBean
#RequestScoped
public class YourConverter implements Converter {
#ManagedProperty...
...
//implementation of converter methods
}
Of course, reading BalusC's invaluable Communication in JSF 2.0 will shed some light on this question as well.
It is also worth mentioning that the scope of your converter bean may be changed to, for example, application or session, if it is not supposed to hold any state.

JSF 2.0 view parameters to pass objects

I am trying to pass an object from one page to another page where each page is in a different view.
On the first page I have an input text, where myBean is a ViewScoped Bean and name is an object.
<h:inputText value="#{myBean.name}"/>
The second page contains,
<f:metadata>
<f:viewParam name="userId" value="#{myBean.name}"/>
</f:metadata>
I get the error Conversion Error setting value mypackage.myBean#257100b' for 'null Converter'.
Can we pass objects other than String values to view parameters?
Yes, you can. You just have to supply a converter which converts between the string representation of the object type behind #{myBean.name} and the real object. The string representation is usually the unique technical/natural identifier of the object in question. For example, the table's PK. It has got to be a string, simply because HTTP request parameters can be strings only. You can't pass complex Java objects around in URLs. URLs are just strings.
Here's a kickoff example:
E.g. the following in the initial view:
<h:link value="Edit" outcome="edit">
<f:param name="id" value="#{personViewer.person.id}" />
</h:link>
(which generates an Edit)
and the following in the linked view:
<f:metadata>
<f:viewParam name="id" value="#{personEditor.person}"
converter="#{personConverter}" converterMessage="Bad request. Unknown person."
required="true" requiredMessage="Bad request. Please use a link from within the system."
/>
</f:metadata>
<h:messages />
with this converter
#ManagedBean
#RequestScoped
public class PersonConverter implements Converter {
#EJB
private PersonService personService;
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return String.valueOf(((Person) value).getId());
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
return personService.find(Long.valueOf(value));
}
}
(oversimplified; all null/number checks omitted, but you got the idea)
See also:
Communication in JSF 2.0 - Converting and validating GET request parameters
Update as per the comments, you actually want to fire a POST request. You don't need a <f:viewParam> at all. It's for setting/validating/converting GET request parameters only. Attempting to serialize the whole object makes also no utter sense. Just use #ManagedProperty.
E.g.
#ManagedBean
#ViewScoped
public class PersonEditor implements Serializable {
#ManagedProperty("#{personViewer.person}")
private Person person;
// ...
}
It's only not bookmarkable anymore and not SEO-friendly (but that's the nature of POST, you're probably already for long aware of this). Note that the #{personViewer} bean must by itself also be #ViewScoped (and thus not #ReqestScoped). You also need to make sure that you don't navigate back with a redirect, but just a forward.

Resources