Injection in a converter does not work in JSF 2.3 - jsf

Server: Payara 5.183.
When the converter is used, a NullPointerException is raised because the injected EJB is null (System.out.println prints "null").
It works (injection not null) if I use a workaround used before JSF 2.3: replacement of #FacesConverter by #Name.
Converter:
#FacesConverter(value = "compteConverter", managed = true)
public class CompteConverter implements Converter<CompteBancaire> {
#EJB
private GestionnaireCompte gestionnaireCompte;
#Override
public CompteBancaire getAsObject(FacesContext context, UIComponent component, String id) {
if (id == null || id.isEmpty()) {
return null;
}
try {
System.out.println("*****EJB gestionnaireCompte=" + gestionnaireCompte);
return gestionnaireCompte.getCompte(Long.parseLong(id));
} catch (NumberFormatException e) {
throw new ConverterException(new FacesMessage("Id de compte invalide"), e);
}
}
#Override
public String getAsString(FacesContext arg0, UIComponent arg1, CompteBancaire compte) { ... }
Usage of this converter:
<ui:define name="metadata">
<f:metadata>
<f:viewParam name="id" value="#{operations.compte}"
converter="compteConverter"/>
Is it a bug of Mojarra/Payara (managed = true is not working) or can you help me to find my error?

Managed converters don't work by default. To make them work I added a CDI bean annotated by #FacesConfig (for JSF 2.3 to be used) and #ApplicationScoped (it will be a CDI bean with this annotation).

Related

How to use jsf in "namespaced mode"

In a website we want to integrate some snippets provided by jsf-applications, think of a dashboard-app or a "portal-light". While analyzing the requirements we came across a blog-post by Arjan Tjims on jsf 2.3 new features, where he mentioned a new "namespaced mode":
In namespaced mode, which is specifically intended for Portlets but can be used in other environments as well, the partial response is given an id that's taken to be the "naming container id". All predefined postback parameter names (such as "javax.faces.ViewState", "javax.faces.ClientWindow", "javax.faces.RenderKitId", etc) are prefixed with this and the naming separator (default ":"). e.g. javax.faces.ViewState" becomes "myname:javax.faces.ViewState". Namespaced mode is activated when the UIViewRoot instance implements the NamingContainer interface.
Our application might be a usecase for that "namespaced mode", so we want to give it a try.
We built a MyUIViewRoot where we implemented NamingContainer and wrapped the original UIViewRoot-instance. We registered a MyViewHandler in faces-config.xml which handles the wrapping of the ViewRoot. For testing we used a simple counter-app with two <h:form>-elements (seems to be important).
We find that "namespace mode" seems to be activated, eg the javax.faces.ViewState indeed is prepended by some namespace and becomes j_id1:javax.faces.ViewState:0. But both actions do not work any more - the postback request does not restore the View any more but creates a new one. So with our simple approach we are missing something (btw, removing only the implements NamingContainer from MyUIViewRoot the counter-app works fine again).
Is there some documentation, a howto or a working example out there that we have overlooked?
How to activate "namespace mode" correctly? What have we missed to make the postback work again?
How can we make MyUIViewRoot to prepend the ViewState with myNamespace?
The application is running in a payara-5 application server.
Our index.xhtml:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head/>
<h:body>
<h:form id="counterForm">
<h:panelGrid columns="2">
<h:outputLabel value="Counter" />
<h:outputText value="#{counterUiController.counter}" />
</h:panelGrid>
<h:commandButton value="inc" action="#{counterUiController.incAction}">
<f:ajax execute="#form" render="#form" />
</h:commandButton>
</h:form>
<h:form id="resetForm">
<h:commandButton value="reset" action="#{counterUiController.resetAction}">
<f:ajax execute="#form" render=":counterForm" />
</h:commandButton>
</h:form>
</h:body>
</html>
The CounterUiController:
#Named
#ViewScoped
public class CounterUiController implements Serializable {
private int counter;
public int getCounter() {
return counter;
}
public void incAction() {
counter++;
}
public void resetAction() {
counter=0;
}
}
Our UIViewRoot-Implementation:
public class MyUIViewRoot extends UIViewRoot implements NamingContainer, FacesWrapper<UIViewRoot> {
private static final Logger LOG = Logger.getLogger(MyUIViewRoot.class.getName());
private UIViewRoot wrapped;
public MyUIViewRoot(UIViewRoot wrapped) {
this.wrapped = wrapped;
LOG.log(Level.INFO, "new instance created: {0}", this);
}
#Override
public UIViewRoot getWrapped() {
return wrapped;
}
#Override
public String createUniqueId() {
return wrapped==null ? null : wrapped.createUniqueId();
}
#Override
public void setId(String id) {
if( wrapped!=null ) {
wrapped.setId(id);
}
}
// all other methodes delegated to `wrapped` directly
}
Our ViewHandler:
public class MyViewHandler extends ViewHandlerWrapper {
private static final Logger LOG = Logger.getLogger(MyViewHandler.class.getName());
public MyViewHandler(ViewHandler wrapped) {
super(wrapped);
}
#Override
public UIViewRoot createView(FacesContext context, String viewId) {
UIViewRoot retval = super.createView(context, viewId);
retval = wrapIfNeeded(retval);
LOG.log(Level.INFO, "view created: {0}", retval);
return retval;
}
#Override
public UIViewRoot restoreView(FacesContext context, String viewId) {
UIViewRoot retval = super.restoreView(context, viewId);
retval = wrapIfNeeded(retval);
LOG.log(Level.INFO, "view restored: {0}", retval);
return retval;
}
private UIViewRoot wrapIfNeeded(UIViewRoot root) {
if (root != null && !(root instanceof MyUIViewRoot)) {
LOG.log(Level.INFO, "view wrapped: {0}, {1}", new Object[] { root, root.getId() });
return new MyUIViewRoot(root);
} else {
return root;
}
}
}
You need to replace the UIViewRoot not to wrap it.
public class NamespacedView extends UIViewRoot implements NamingContainer {
//
}
And then in faces-config.xml.
<component>
<component-type>javax.faces.ViewRoot</component-type>
<component-class>com.example.NamespacedView</component-class>
</component>
That's basically all. See also the Mojarra IT on this.

JSF in combination with Bean Validation: ConstraintViolationException

I try to use JSF in combination with Bean Validation. Basically, everything works well, the validation works as expected, I get the correct message, but there is an exception on my Glassfish console:
Warnung: EJB5184:A system exception occurred during an invocation on EJB MyEntityFacade, method: public void com.mycompany.testbv.AbstractFacade.create(java.lang.Object)
Warnung: javax.ejb.EJBException
at com.sun.ejb.containers.EJBContainerTransactionManager.processSystemException(EJBContainerTransactionManager.java:748)
....
....
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:544)
at java.lang.Thread.run(Thread.java:744)
Caused by: javax.validation.ConstraintViolationException: Bean Validation constraint(s) violated while executing Automatic Bean Validation on callback event:'prePersist'. Please refer to embedded ConstraintViolations for details.
This exception occurs if I use custom constraints as well as predefined constraints.
Here is my sample code.
Sample Entity:
#Entity
#ValidEntity
public class MyEntity implements Serializable {
private static final long serialVersionUID = 3104398374500914142L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Size(min = 2)
private String name;
public MyEntity(String name) {
this.name = name;
}
public MyEntity() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Custom constraint:
#Constraint(validatedBy = MyValidator.class)
#Target({FIELD, METHOD, TYPE})
#Retention(RUNTIME)
public #interface ValidEntity {
String message() default "fail";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Custom validator:
public class MyValidator implements ConstraintValidator<ValidEntity, MyEntity>{
#Override
public void initialize(ValidEntity a) {
}
#Override
public boolean isValid(MyEntity t, ConstraintValidatorContext cvc) {
return false;
}
}
Sample Controller:
#Named
#SessionScoped
public class MyController implements Serializable {
private static final long serialVersionUID = -6739023629679382999L;
#Inject
MyEntityFacade myEntityFacade;
String text;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public void saveNewEntity() {
try {
myEntityFacade.create(new MyEntity(text));
} catch (Exception e) {
Throwable t = e;
while (t != null) {
if (t instanceof ConstraintViolationException) {
FacesContext context = FacesContext.getCurrentInstance();
Set<ConstraintViolation<?>> constraintViolations = ((ConstraintViolationException) t).getConstraintViolations();
for (ConstraintViolation<?> constraintViolation : constraintViolations) {
FacesMessage facesMessage = new FacesMessage(constraintViolation.getMessage());
facesMessage.setSeverity(FacesMessage.SEVERITY_ERROR);
context.addMessage(null, facesMessage);
}
}
t = t.getCause();
}
}
}
}
Sample jsf page:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head></h:head>
<h:body>
<h:form>
<h:messages id="messages" />
<h:inputText value="#{myController.text}" />
<h:commandButton value="Save" action="#{myController.saveNewEntity()}" />
</h:form>
</h:body>
</html>
The MyEntityFacade only calls persist from entity manager.
As mentioned before, the application is running fine and the correct messages are shwon, but I want to avoid this exception in the Glassfish console.
Setting the validation mode in persistence.xml to NONE as discussed here is no option, because I want a validation.
I use JSF in version 2.2, the implementation is Mojarra. The version of Bean Validation is 1.1, the implementation is Hibernate Validator.
Application Server is Glassfish 4.0.
Class-level constraints do not work with JSF. Take a look at this answer. When you press the 'Save' button JSF checks only if name has at least 2 chars and does not take into account the ValidEntity constraint. JPA, on the other hand, complains that the bean is not valid and throws an exception.
UPDATE
1) the #Size constraint is on MyEntity.name property while in the facelet you have MyController.text property. In the JSF perspective there is nothing to validate. It has no knowledge of the MyEntity at all.
2) ValidEntity is always invalid, so JPA will always throw the exception (unless you disable validation) even if you properly set the MyEntity.name in the facelet.

#Ejb doesn't inject in converter

I'm trying to Write my own converter
I want to Inject my EJBs into my converter with #EJB .
my #EJB works in Other my ManagedBean but it doesn't work here in my converter
#ManagedBean
#ViewScoped
public class ServerTypeConverter implements Converter {
#EJB
private ServerTypeFacade serverTypeFacade;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null || value.isEmpty()) {
return null;
} else {
int id = Integer.parseInt(value);
return serverTypeFacade.findById(id);
}
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null || value.equals("")) {
return "";
} else {
Integer id = ((ServerType) value).getServer_type_id();
return String.valueOf(id);
}
}
}
ServerTypeFacade returns null. why ?
here is the way i use converter
<h:selectOneMenu value="#{serverMB.selectedServerType}">
<f:converter converterId="serverTypeConverter"/>
<f:selectItems value="#{serverMB.serverTypesList}" var="servertypes" itemLabel="#{servertypes.server_type_name}" />
<f:ajax listener="#{serverMB.changeServerType}" render="dd" />
</h:selectOneMenu>
The #EJB in converter works only if you declare it as a managed bean by #ManagedBean and use it as managed bean by #{serverTypeConverter}.
However, you're using the converter as a faces converter by converterId="serverTypeConverter". Apparently you've also declared it as a faces converter by #FacesConverter on the class or <converter> in faces-config.xml.
Those two ways of declaring and using the converter are mutually exclusive. In order to get #EJB to work, you should be using the converter as a managed bean:
<f:converter binding="#{serverTypeConverter}" />
See also:
CDI Injection into a FacesConverter

EJB converter and lost bean injection

I have an application written in Java EE with EJB and JSF. I wanted to create a JSF converter which has an EJB injected:
#ManagedBean(name="addressConverter")
#RequestScoped
public class AddressConverter implements Converter {
#EJB(name = "AddressDao")
private AddressDao addressDao;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
return addressDao.find(Long.valueOf(value));
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return String.valueOf(((Address) value).getId());
}
}
When in application any exception appears, the injected AddressDao is missing and I get the meeste that "...env.../AddressDao" is missing (sorry, I can't get the exact error right now).
How do I handle that?

ManagedProperty not injected in #FacesConverter

I'm trying to inject a ManagedBean in my FacesConverted the following way:
#ManagedBean
#RequestScoped
#FacesConverter(forClass = Group.class)
public class GroupConverter implements Converter {
#ManagedProperty("#{groupService}")
private GroupService groupService;
#Override
public Group getAsObject(FacesContext context, UIComponent arg1,
String groupName) {
return groupService.findGroupByName(groupName);
}
#Override
public String getAsString(FacesContext arg0, UIComponent arg1, Object group) {
return ((Group) group).getName();
}
public GroupService getGroupService() {
return groupService;
}
public void setGroupService(GroupService groupService) {
this.groupService = groupService;
}
}
The problem is that groupService isn't being injected and I get a NullPointerEx. Shouldn't it be autowired automatically since it's also a ManagedBean? It all works when I change "getAsObject" to "return new Group();" obviously.
Any ideas?
It is likely that you are not resolving the managed bean name.
#ManagedBean(name = "myConverter")
#RequestScoped
#FacesConverter(value = "myConverter")
public class MyConverter implements Converter {
For example, consider these two components:
<h:inputText converter="myConverter" value="#{foo.prop}" />
<h:inputText converter="#{myConverter}" value="#{bar.prop}" />
When the converter is set on the first component, it will be created by Application.createConverter. A converter is not a managed bean. The same rules apply if you match a converter by type.
In the second component, a value expression is used to return a class that implements Converter. This uses the usual managed bean mechanisms. In this case, the #FacesConverter annotation is irrelevant.

Resources