my environment is: Maven 3, JSF 2, Tomcat 7, Java 6. I am trying to use the java #Named annotations for my beans, that I use in JSF. The project is simple, with one TestBean that has one getter and setter for one property called text.
#Named
public class TestBean {
String text = "Hello World"
public String getText() {
return this.text;
}
public void setText(String text) {
this.text = text;
}
}
In my jsf file when I try:
#{testBean.text}
I get no error but also nothing is shown. What do I have to do to be able to access the bean?
#Named annotation only exposes your bean in EL context, it doesn't give it a scope. This defaults to 'dependent' pseudo-scope. Dependent beans are not managed, they are just inserted at injection points and that's it.
Try explicitly adding scope. Also you can use built-in #Model stereotype, which is simply a combination of #Named and #RequestScoped.
Related
Let's suppose I have following structure:
1) Managed Bean:
#ViewScoped
#ManagedBean
public class TestBean {
private Test test;
//getters/setters
}
2) Test class:
public class Test {
private String attribute;
//gets/sets
}
3) XHTML
<p:inputText id="test" value="#{testBean.test.atribute}" />
Now, I know there is a way to find and get component instance:
UIComponent c = view.findComponent(s);
From UIComponent, how do I get the type bound to component?
What I need is to get full qualified class name from what is set as "value" attribute in component. Something like: package.Test.attribute.
UIComponent offers getValueExpression("attributeName")
sample :
UIViewRoot viewRoot = Faces.getViewRoot();
UIComponent component= viewRoot.findComponent("x");
ValueExpression value = component.getValueExpression("value");
Class<?> expectedType = value.getType(Faces.getELContext());
NB:Faces here is from Omnifaces, which is a "Collection of utility methods for the JSF API that are mainly shortcuts for obtaining stuff from the thread local FacesContext. "
excepts from getType() javadoc
public abstract Class getType(ELContext context) Evaluates the
expression relative to the provided context, and returns the most
general type that is acceptable for an object to be passed as the
value parameter in a future call to the setValue(javax.el.ELContext. java.lang.Object) method. This is not always the same as
getValue().getClass(). For example, in the case of an expression that
references an array element, the getType method will return the
element type of the array, which might be a superclass of the type of
the actual element that is currently in the specified array element.
For MethodExpression read this.
While having a custom qualifier for CDI support as followed:
#Qualifier
#Retention(RUNTIME)
#Target({METHOD, FIELD, PARAMETER, TYPE})
public #interface QualifiedFooBean {
}
#QualifiedFooBean
public class FooBean implements ImplFooBean {
}
public interface ImplFooBean {
}
I would like to bind FooBean #{fooBean} directly without requiring a wrapper or processor (seen from this example). The annotation "Named" (in class FooBean) seems not to work for my class layout.
My solution (without wrapper) which I'm wondering why it's not working and invoking: Target Unreachable, identifier 'fooBean' resolved to null
#Named
#QualifiedFooBean
public class FooBean implements ImplFooBean {
}
Has anyone any idea?
A wrapper is not needed. My solution is perfectly valid. It's also allowed to add Named in combination of a custom qualifier (in my case QualifiedFooBean). I had to just create an empty beans.xml file in WEB-INF folder in order to get CDI to work. Anyhow The question itself explains how custom qualifiers can work. You can also prefill beans.xml with following content:
<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>
It will serve as a skeleton for future use, if you need to configure more fancy stuff with CDI.
Adding #Named to your bean should work : it works for me.
In Fact #Named is a qualifier, when JSF resolve the Bean for displaying it does a lokup based on #Named qualifier. In CDI a bean is found if the lookup side (i.e. Injection point) ask for a subset of its Qualifier.
For instance a bean qualified like that :
#QualifiedFooBean
#SecondQualifier
public class FooBean {}
Will be found (if there is no ambiguous resolution) by
#Inject
#SecondQualifier
FooBean bean;
or even :
#Inject
FooBean bean;
But not by
#Inject
#SecondQualifier
#ThirdQualifier
FooBean bean;
So you can add the #Named qualifier and let CDI resolution engine do its job.
When using CDI and JSF2 How can a HTTP request parameter be injected into a bean?
HINT: before reading any further have a look at http://showcase.omnifaces.org/cdi/Param.
Do it yourself is probably obsolete seeing how omnifaces is a de facto standard today. I would probably not have written this if omnifaces had this at the time
CDI does not solve specialized problems like injecting a request parameter. That's supposed to be solved by extensions.
This is already provided by solder. http://docs.jboss.org/seam/3/solder/latest/reference/en-US/html/injectablerefs.html
It will probably be included in Deltaspike 0.4-incubating or similar as well.
That said the code required is rather simple to implement yourself. Example below:
Annotation to use for the injection point (For example private String myParam;)
import javax.enterprise.util.Nonbinding;
import javax.inject.Qualifier;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
#Qualifier
#Retention(RUNTIME)
#Target({METHOD, FIELD, PARAMETER })
public #interface RequestParam {
#Nonbinding
public String value() default "";
}
Now we have the annotation but we can't just ask the container to dependency inject a #RequestParam - we obviously need an implementation.
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
public class RequestParamProducer implements Serializable {
private static final long serialVersionUID = -4260202951977249652L;
#Inject
FacesContext facesContext;
// Producer for #RequestParam
#Produces
#RequestParam
String getRequestParameter(InjectionPoint ip) {
String name = ip.getAnnotated().getAnnotation(RequestParam.class)
.value();
if ("".equals(name))
name = ip.getMember().getName();
return facesContext.getExternalContext().getRequestParameterMap()
.get(name);
}
}
So how does it work? Well quite simply it first checks if you did specify what parameter you wanted as in #Requestparam("longAndTerribleFieldNameBestToSpecify");
If you didn't it will use the fieldName. So if you annoted a setter called setMyInstance it will look for a parameter called setMyInstance.
The normal use case would be to have a String variable that is named exactly like the parameter you want.
Note that we inject FacesContext, that must also be produced. A FacesContext producer could look like this:
class FacesContextProducer {
#Produces #RequestScoped FacesContext getFacesContext() {
return FacesContext.getCurrentInstance();
}
}
End usage:
#Inject
#RequestParam
private String session_secret;
Note that this will not work for Servlet or similar as it requires access to FacesContext. In those cases one need to wrap the injection with for example a bean that is #RequesScoped. You inject that bean instead.
Two days ago I wrote this question:
How can I retrieve an object on #WindowScoped?
and BalusC answered with some suggestions, now I have some problem to understand if my problem is that the object in WindowScoped is stored properly or my code to retrieve it is wrong!
Well, as I said, I have an object that I stored in #WindowScoped annotation but I can retrive this object only the first time! Why?
I just have a doubt: the CODI extension of MyFaces could be configured in some manner? Or I can use it simple adding the jar files to my project?
However, these are parts of my code because I don't know where is the problem:
LogicBean.java (the object that I should retrive):
#ManagedBean (name="logicBean" )
#WindowScoped
public class LogicBean implements Serializable
{
String pageIncluded;
// getter and setter methods
public String action(String value)
{
setPageIncluded(value);
return "include";
}
}
include.xhtml:
<ui:include src="#{logicBean.pageIncluded}"/>
ProgettiController.java
#ManagedBean(name = "progettiController")
#SessionScoped
public class ProgettiController implements Serializable {
private FacesContext context = FacesContext.getCurrentInstance();
private LogicBean logicBean = context.getApplication().evaluateExpressionGet(context, "#{logicBean}", LogicBean.class);
//getter and setter methods
public void testMethod()
{
logicBean.action("WEB-INF/jsf/page1.xhtml");
}
}
I tried also using #ManagedProperty("#{logicBean}") and setting the scope as WindowScoped but nothing change...
EDIT: after some new trials I found a strange problem, on my include.xhtml I added #{progettiController.logicBean.getPageIncluded()} and #{logicBean.getPageIncluded()} for check these two fields o?
Well, when I load the application for the first time the variables are correctly set and I see what I want, the second time the first variable is setted with the new value but the second is empty and I don't see anything, but now is coming the strange thing... if I should try again the app I should open index.xhtml where I had some forms like this:
<h:form>
<h:commandLink action="#{logicBean.action('/WEB-INF/jsf/progetti/List.xhtml')}" value="Show All Progetti Items"/>
</h:form>
and which is the result?
The first variable remains set with the old value (wrong) but the second is setted correctly so I can review the page like I would!
If someone can help me I will thank him/her forever!
CODI is an extension to CDI, so you should manage your beans by CDI #Named annotation instead of the JSF #ManagedBean annotation. Then you can inject the other bean by CDI #Inject annotation. The following example should work:
import javax.inject.Named;
import org.apache.myfaces.extensions.cdi.core.api.scope.conversation.WindowScoped;
#Named
#WindowScoped
public class LogicBean implements Serializable {
// ...
}
and
import javax.enterprise.context.SessionScoped;
import javax.inject.Inject;
import javax.inject.Named;
#Named
#SessionScoped
public class ProgettiController implements Serializable {
#Inject
private LogicBean logicBean;
// ...
}
From just a few searches, this seems like a problem that has been around for a while. I have written a FacesConverter that looks like the following. The object Category is a JPA entity and CategoryControl is the DAO that fetches it.
#FacesConverter(value = "categoryConverter")
public class CategoryConverter implements Converter {
#Inject private CategoryControl cc;
public CategoryConverter() { }
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (cc != null) return cc.getByName(value);
System.out.println("CategoryConverter().getAsObject(): no injection!");
return null;
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (!(value instanceof Category)) return null;
return ((Category) value).getName();
}
}
As you probably guessed by now, I never get the injection. I got this workaround from this page, which looks like this.:
Workaround for this problem: create this method in your localeController:
public Converter getConverter()
{
return FacesContext.getCurrentInstance().getApplication().createConverter("localeConverter");
}
and use converter="#{localeController.converter}" in your h:selectOneMenu.
However I can't make this work either. My backing bean creates and returns a converter all right, but it doesn't get the object injected into it.
I am using MyFaces CODI 1.0.1. With the current GlassFish/Weld container. Can anyone suggest a solution before I re-code to not use a Converter?
Replace
#FacesConverter(value = "categoryConverter")
by
#Named
and use
<h:inputSomething converter="#{categoryConverter}" />
or
<f:converter binding="#{categoryConverter}" />
instead of
<h:inputSomething converter="categoryConverter" />
or
<f:converter converterId="categoryConverter" />
By the way, similar problem exist for #EJB inside a #FacesConverter. It however offers a way to be grabbed by JNDI manually. See also Communication in JSF 2.0 - Getting an EJB in #FacesConverter and #FacesValidator. This way you can use a #FacesConverter(forClass=Category.class) without manually defining it everytime. Unfortunately I can't tell from top of head how to realize that for CDI beans.
Update: if you happen to use JSF utility library OmniFaces, since version 1.6 is adds transparent support for using #Inject and #EJB in a #FacesConverter class without any additional configuration or annotations. See also the CDI #FacesConverter showcase example.
The #Inject Annotation only works in CDI managed instances. If you want to use CDI features inside a non-CDI managed instance (Like a JSF Validator or a JSF Converter) you can just programm against the CDI API.
This works only in at least Java EE 7 + CDI 1.1 server.
#FacesValidator("userNameValidator")
public class UserNameValidator implements Validator {
private UserService userService;
public UserNameValidator(){
this.userService = CDI.current().select(UserService.class).get();
}
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
....
}
}
https://docs.oracle.com/javaee/7/api/javax/enterprise/inject/spi/CDI.html
With all the AnnotationHell in Java EE people forget how to code.
Just use #Advanced of CODI for your #FacesConverter see the Wiki.
As soon as a converter or a validator is annotated with #Advanced it's possible to use #Inject.
Per BalusC's answer here, I decided to add JSF (requestscoped) managed beans that only contained #FacesConverter and Converter to resolve this issue in my app, since I'm migrating from JSF managed beans to CDI managed beans.
I tried CODI #Advanced against #FacesConverter, but it does not inject the bean at all.