Use ManagedBean in FacesConverter - jsf-2

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.

Related

#FacesConverter without converter-id and forClass

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.

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;

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.

PostConstruct/Constructor for FacesComponent

I've created FacesComponent which I want to initialize in constructor/postconstructor. The problem is that getAttributes() is empty in there. Below is the example.
#FacesComponent("articleComponent")
public class ArticleFacesComponent extends UINamingContainer {
private Article article;
public ArticleFacesComponent() {
Object idObj = getAttributes().get("articleId"); // I want to get article id to initialize object but getAttributes() is empty
...
article = em.find(Article.class, id);
}
}
You need to perform the job in the encodeAll() method. It's invoked during render response, when the component is about to be rendered.
#Override
public void encodeAll(FacesContext context) throws IOException {
// Here.
super.encodeAll(context);
}
Given that you're extending from UINamingContainer, you're most likely creating a backing component for a composite component. In that case, this article should provide you useful insights to get started: composite component with multiple input fields.
Unrelated to the concrete problem, accessing the DB in a component is a smell. Rather pass a fullworthy Article instance as component value instead of its ID. The component/renderer should only deal with front-end (HTTP/HTML) stuff based on the model, not with back-end (DB/SQL) stuff. You should provide the component exactly the model it needs to work with.
Like #BalusC said. But if you are using PrimeFaces you have to overwrite the encodeBegin() method. Else you may overwrite encodeAll().

How can I retrieve an object on #WindowScoped?

In this post Dynamic ui:include I asked how I could store an object in some state that could permit me to load a new windows, or tab, of the same browser and it was not stored also in the new windows. Adrian Mitev told me to use #WindowScoped, an option of MyFaces extension called CODI and i tried to implement it.
Now I should say that I'm blind and when I tried to open Apache Wiki my browser crashes on many pages so I can't read the guides.
However I add the source code on my project and the compiler didn't give any errors.
The problem is that now thepage when I try to retrive the bean that I stored by #WindowScoped doesn't work properly!
I use this code in my bean:
#ManagedBean (name="logicBean" )
#WindowScoped
In include.xhtml I retrieve the parameter with this code:
<ui:include src="#{logicBean.pageIncluded}"/>
And in my other beans I retrieve the LogicBean with this code (and I'm sure that the problem is on this code)
LogicBean l = (LogicBean) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("logicBean");
How can I retrive the "correct" LogicBean object?
You're trying to get the LoginBean from the session map. This works only for session scoped beans with the standard JSF #SessionScoped annotation.
The canonical way to access other beans is using #ManagedProperty on the retrieving bean.
E.g.
#ManagedBean
#RequestScoped
public class OtherBean {
#ManagedProperty("#{logicBean}")
private LogicBean logicBean;
// Getter+Setter.
}
If you really need to access it inside the method block by evaluating the EL programmatically, you should be using Application#evaluateExpressionGet() instead:
FacesContext context = FacesContext.getCurrentInstance();
LogicBean logicBean = context.getApplication().evaluateExpressionGet(context, "#{logicBean}", LogicBean.class);
// ...

Resources