JVM Version: 14.0.2+12-46
myfaces-api 2.3.9
myfaces-impl 2.3.9
apache-tomee-webprofile-8.0.4
I am using the following converter for an simple entity class:
package view;
import entities.Chain;
import managed.Chains;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;
import javax.inject.Inject;
import javax.inject.Named;
#Named
#FacesConverter(value = "chainConverter", managed = true)
public class ChainConverter implements Converter<Chain> {
#Inject
Chains chains;
#Override
public Chain getAsObject(FacesContext context, UIComponent component, String value) {
if (value != null && !value.trim().isEmpty()) {
Chain found = chains.getAvailable().stream()
.filter(chain -> chain.getAbbreviation().equals(value))
.findFirst()
.orElse(null);
return found;
}
return null;
}
#Override
public String getAsString(FacesContext context, UIComponent component, Chain chain) {
if (chain != null && !chain.toString().isEmpty()){
return chain.getAbbreviation();
}else{
return null;
}
}
}
Given the example at: https://www.primefaces.org/showcase/ui/input/oneMenu.xhtml
I try to use the converter as follows:
<p:selectOneMenu id="chain" value="#{chains.chain}" var="c" converter="chainConverter">
<f:selectItem itemLabel="== Selecte Chain ==" />
<f:selectItems value="#{chains.available}" var="chain" itemLabel="#{chain.name}" itemValue="#{chain}" />
<p:column>
#{c.name}
</p:column>
<p:ajax event="valueChange" listener="#{inventories.refreshSurvey}" update="#form" />
</p:selectOneMenu>
The above "converter=" reference produces the following error:
org.primefaces.application.exceptionhandler.PrimeExceptionHandler.logException null
java.lang.NullPointerException
at org.apache.myfaces.cdi.converter.FacesConverterCDIWrapper.getAsString(FacesConverterCDIWrapper.java:69)
at org.primefaces.renderkit.SelectRenderer.getOptionAsString(SelectRenderer.java:207)
at org.primefaces.component.selectonemenu.SelectOneMenuRenderer.encodeOption(SelectOneMenuRenderer.java:561)
at org.primefaces.component.selectonemenu.SelectOneMenuRenderer.encodeSelectItems(SelectOneMenuRenderer.java:524)
at org.primefaces.component.selectonemenu.SelectOneMenuRenderer.encodeHiddenSelect(SelectOneMenuRenderer.java:211)
If I change the converter reference to an el expression as follows:
converter="#{chainConverter}"
I get the error:
org.primefaces.application.exceptionhandler.PrimeExceptionHandler.logException Cannot convert [entities.Chain#928f] of type [class java.lang.String] to [class entities.Chain]
javax.el.ELException: Cannot convert [entities.Chain#928f] of type [class java.lang.String] to [class entities.Chain]
at org.apache.el.lang.ELSupport.coerceToType(ELSupport.java:548)
at org.apache.el.parser.AstValue.setValue(AstValue.java:203)
at org.apache.el.ValueExpressionImpl.setValue(ValueExpressionImpl.java:263)
at org.apache.webbeans.el22.WrappedValueExpression.setValue(WrappedValueExpression.java:93)
at org.apache.webbeans.el22.WrappedValueExpression.setValue(WrappedValueExpression.java:93)
However, the weirdest thing is that in one project it works while in the other project it does not. For example in this sample project it works as expected: https://github.com/dannymk/PrimefacesTest
I have a nagging suspicion that there is a difference in a library version someplace in this project. However, I really can't pinpoint the problem even after changing the pom file to match a working projects pom file. This is definitely a head scratcher to me.
Related
I try to validate a p:selectOneMenu using the validator attribute, but in the validate method the value of object is null. I don´t understand why it is null, somebody could help me. I´m using PrimeFaces 6.2.
My xhtml is:
<p:selectOneMenu id="somFieldType"
widgetVar="somFieldType"
converter="entityConverter"
required="false"
value="#{paramDetail.article_field_type}"
validator="#{detailArticleBean2.isTypeValid}"
... >
<f:selectItem itemLabel="#{i18n['avocado.general.seleccionar']}" itemValue="0"/>
<f:selectItem itemLabel="#{i18n['avocado.general.texto']}" itemValue="1"/>
...
</p:selectOneMenu>
In my bean I´m only printing the value:
public void isTypeValid(FacesContext ctx, UIComponent component, Object value) throws ValidatorException {
System.out.println(value.toString());
}
The console returns an error:
javax.el.ELException: /Ref/Art/artDetail.xhtml #165,67 validator="#{detailArticleBean2.isTypeValid}": java.lang.NullPointerException
at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:111)
I´d appreciate your help.
Your value is null, so you cannot apply .toString(). Use something like:
public void isTypeValid(FacesContext ctx, UIComponent component, Object value) throws ValidatorException {
if (value == null) {
// Value is unset; don't validate
return;
}
// Value is set; do validation
...
}
See also:
What is a NullPointerException, and how do I fix it?
I have seen a few postings related to this. However, I seem to be seeing something that I do not understand. Following is a section of my code:
package com.mvctest;
import java.util.List;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.dao.User;
#Controller
public class LoginController {
#RequestMapping("/newAccount")
public String createAccountScreen (Model model) {
model.addAttribute("user", new User());
return "createaccountscreen";
}
#RequestMapping(value="/createAccount", method=RequestMethod.POST)
public String createAccount (#Valid User user, BindingResult result) {
if (result.hasErrors()) {
List<ObjectError> errors = result.getAllErrors();
for(ObjectError error : errors) {
System.out.println(error.getDefaultMessage());
}
return "createaccountscreen";
}
return "accountcreated";
}
Then in my jsp file createaccountscreen.jsp I have the following:
<sf:form method="post"
action="${pageContext.request.contextPath}/createAccount"
commandName="user">
This works fine.
However if I changed the addAttribute () call as following
model.addAttribute("myuser", new User());
and changed the commandName in the jsp to
<sf:form method="post"
action="${pageContext.request.contextPath}/createAccount"
commandName="myuser">
I get the following error
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'myuser' available as request attribute
Essentially, I replace 'user' with 'myuser'.
Bean class is 'User'. Does that mean the commandName needs to be 'user'?
We secure our REST services (for server to server communication, no user involved) with Spring Security OAuth2. However when one tries to access a protected resource in a browser, it will show:
<oauth>
<error_description>
An Authentication object was not found in the SecurityContext
</error_description>
<error>unauthorized</error>
</oauth>
We want this to be a custom page of our own choosing. Is there a way?
Setting the access-denied-page won't do. For one it requires the definition of a login page which we don't have as this is a pure server to server communication. For another this attribute is supposedly deprecated since Spring 3.0.. or something.
Anyway.. Debugged my way into the OAuth Error Handling. And found that the response seems to somehow get enriched with the information I see on the error page. Apparently no page rendering at all is done so it looks like there is no error page to replace..?!
At least we want to hide the fact that we use OAuth and just display a basic "Denied" text if we can't have a "real" page.. So maybe I'll have to extend the spring security handler or add a custom filter to modify the response?!
Maybe a redirect to our error page?
Thanks!
Edit
For our current setup check my other SO question here
I had to remove the oauth detail too and my solution was to implement my own OAuth2ExceptionRenderer
package org.some.nice.code;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.ResponseEntity;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.security.oauth2.provider.error.OAuth2ExceptionRenderer;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletWebRequest;
public class HeaderOnlyOAuth2ExceptionRenderer implements OAuth2ExceptionRenderer
{
private final Log logger = LogFactory
.getLog(MyOAuth2ExceptionRenderer.class);
public void handleHttpEntityResponse(HttpEntity<?> responseEntity,
ServletWebRequest webRequest) throws Exception
{
if (responseEntity == null)
{
return;
}
HttpInputMessage inputMessage = createHttpInputMessage(webRequest);
HttpOutputMessage outputMessage = createHttpOutputMessage(webRequest);
logger.info("filtering headers only...");
if (responseEntity instanceof ResponseEntity
&& outputMessage instanceof ServerHttpResponse)
{
((ServerHttpResponse) outputMessage)
.setStatusCode(((ResponseEntity<?>) responseEntity)
.getStatusCode());
}
HttpHeaders entityHeaders = responseEntity.getHeaders();
if (!entityHeaders.isEmpty())
{
outputMessage.getHeaders().putAll(entityHeaders);
}
}
private HttpInputMessage createHttpInputMessage(NativeWebRequest webRequest)
throws Exception
{
HttpServletRequest servletRequest = webRequest
.getNativeRequest(HttpServletRequest.class);
return new ServletServerHttpRequest(servletRequest);
}
private HttpOutputMessage createHttpOutputMessage(
NativeWebRequest webRequest) throws Exception
{
HttpServletResponse servletResponse = (HttpServletResponse) webRequest
.getNativeResponse();
return new ServletServerHttpResponse(servletResponse);
}
}
Then you will have to reference it within your spring context
<bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="theRealm" />
<property name="exceptionRenderer" ref="headerOnlyExceptionRender" />
</bean>
<bean id="clientAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="theRealm/client" />
<property name="typeName" value="Basic" />
<property name="exceptionRenderer" ref="headerOnlyExceptionRender" />
</bean>
<bean id="oauthAccessDeniedHandler" class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler">
<property name="exceptionRenderer" ref="headerOnlyExceptionRender" />
</bean>
<bean id="headerOnlyExceptionRender" class="org.some.nice.code.HeaderOnlyOAuth2ExceptionRenderer"/>
Hope it helps.
I have made a new Seam validator:
package validators;
import java.io.Serializable;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.faces.validator.ValidatorException;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.faces.Validator;
import org.jboss.seam.annotations.intercept.BypassInterceptors;
import org.jboss.seam.log.Log;
import org.jboss.seam.log.Logging;
#Name("roCountyValidator")
#Validator
#BypassInterceptors
public class RoCountyValidator implements javax.faces.validator.Validator,
Serializable {
/**
*
*/
private static final long serialVersionUID = -3876319398131645955L;
Log log = Logging.getLog(RoCountyValidator.class);
public void validate(FacesContext context, UIComponent component,
Object value) throws ValidatorException {
log.info("validating....!");
if (String.valueOf(value).equals("Arad"))
((UIInput) component).setValid(true);
else {
((UIInput) component).setValid(false);
FacesMessage message = new FacesMessage();
message.setDetail("Invalid county");
message.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(message);
}
}
}
The problem is that I do not know how to use it directly from jsf...
The following does not work....
I have declared it in a special taglib file: myvalidators.taglib.xml
<facelet-taglib>
<namespace>http://example.com/jsf/my/validators</namespace>
<tag>
<tag-name>roCountyValidator</tag-name>
<validator>
<validator-id>roCountyValidator</validator-id>
</validator>
</tag>
and tried to use it like:
<h:inputText id="someField" value="#{booking.creditCardName}"
required="true" label="County">
<my:roCountyValidator/>
<h:message for="someField"/>
</h:inputText>
Can you tell me where I am wrong?
Thanks.
Two ways to solve this.
One, is to use as #BalusC has written.
You don't need to define anything in faces-config.xml
<h:inputText id="cc" required="true" value="#{booking.creditCardName}">
<f:validator validatorId="roCountyValidator"/>
<f:attribute name="oldCreditCardNumber" value="#{booking.creditCardName}" />
<s:validate />
</h:inputText>
Here you can even bind the old credit card number, if you want to check that also.
Then in your validate method:
public void validate(FacesContext context, UIComponent component,
Object value) throws ValidatorException {
log.info("validating....!");
String oldCreditcard = String.valueOf(component.getAttributes().get("oldCreditCardNumber"));
String newCreditCard = (String) value;
if(SomeClass.isCorrectCreditcard(newCreditCard)) {
//You don't need to setValid(false), this is done automatically
Map<String, String> messages = Messages.instance();
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, messages.get("wrongCreditCardNumber"), messages
.get("wrongCreditCardNumber")));
}
}
The other way, is to use the validator tag in <h:inputText>
You don't even need to create a #Validator class, as long as it's a seam component and if you use the same method signature.
I use a validator component for all my general validators
#Name("validator")
#Scope(ScopeType.EVENT)
#BypassInterceptors
public class Validator {
public void positiveInteger(FacesContext context, UIComponent toValidate, Object value) {
String val = (String) value;
try {
int v = Integer.parseInt(val);
if (v < 0)
throw new NumberFormatException();
} catch (NumberFormatException e) {
((UIInput) toValidate).setValid(false);
FacesMessages.instance().addToControlFromResourceBundle(toValidate.getId(), "invalid.integer");
}
}
}
Now you can add the validator:
<h:inputText value="#{foo.bar}" required="true" validator="#{validator.positiveInteger}">
<s:validate/>
<h:inputText>
I have no idea about the Seam part, it might have different approaches for this, but in standard JSF, you normally define it as <validator> in faces-config.xml.
<validator>
<validator-id>roCountyValidator</validator-id>
<validator-class>validators.RoCountyValidator</validator-class>
</validator>
and use it as follows:
<h:inputText>
<f:validator validatorId="roCountyValidator" />
</h:inputText>
Solution found:).
Forget about taglibs and stuff!
Use it like:
<h:inputText id="someField" value="#{booking.creditCardName}"
required="true" label="County" validator="roCountyValidator">
<h:message for="someField"/>
</h:inputText>
Please remark that
validator="roCountyValidator"
it shouldn't be used like EL expression !!! (my first wrong decision)
So the advantage of using Seam + #Validator: Seam will transform that component in the background to a jsf validator so you do no longer need jsf validator tags or any other configuration in faces-config.xml.
I'm trying to build some select boxes with dependencies on other select boxes. I'm fairly new to Seam - just to add this. Here is my JSF - for test purposes I'm trying to display a dataTable
<f:facet name="header">Profil</f:facet>
<a:form ajaxSubmit="true" reRender="testTable">
<s:decorate id="techTypeField" template="layout/edit.xhtml">
<ui:define name="label">Choose tech</ui:define>
<h:selectOneRadio id="techType" value="#{technologyType}" onchange="submit()">
<s:selectItems value="#{technologyTypes}" var="elem" label="#{elem.name}" id="typeId"/>
<s:convertEntity/>
</h:selectOneRadio>
</s:decorate>
</a:form>
<div style="clear:both"/>
<h:dataTable var="productLine" id="testTable" value="#{productLines}" rendered="#{productLines.rowCount > 0}">
<h:column >
<f:facet name="header">pl</f:facet>
#{productLine.id}
</h:column>
</h:dataTable>
My backing bean
package de.ac.dmg.productfinder.workflow;
import java.util.List;
import javax.ejb.Remove;
import javax.ejb.Stateful;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.jboss.seam.annotations.Factory;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.datamodel.DataModel;
import org.jboss.seam.annotations.datamodel.DataModelSelection;
import org.jboss.seam.international.StatusMessages;
import org.jboss.seam.log.Log;
import de.ac.entity.Machine;
import de.ac.entity.ProductLine;
import de.ac.entity.TechnologyType;
#Stateful
#Name("Profil")
public class ProfilBean implements IProfil {
#PersistenceContext
private EntityManager entityManager;
#Logger
private Log log;
#DataModel(value = "technologyTypes")
private List<TechnologyType> technologyTypes;
#DataModelSelection(value = "technologyTypes")
private TechnologyType technologyType;
#DataModel(value = "productLines")
private List<ProductLine> productLines;
#DataModel(value = "machines")
List<Machine> machines;
#In
StatusMessages statusMessages;
// add additional action methods
#Factory("technologyTypes")
public void loadTechTypes() {
technologyTypes = entityManager.createQuery("Select t from TechnologyType t").getResultList();
}
#Factory("productLines")
public void loadProductLinies() {
System.out.println(technologyType);
productLines = entityManager.createQuery("select p from ProductLine p where p.technologyType = :type ")
.setParameter("type", technologyType).getResultList();
}
public void next() {
// implement your business logic here
log.info("Profil.next() action called with: #{Profil.value}");
statusMessages.add("next #{Profil.value}");
}
#Remove
public void destroy() {
}
}
I can set the tech type on time - after one click it isn't refreshed sadly. What I'm doing wrong here?
When selecting a 'technology type', you need an action which reloades the product types based on the selected techtype. Something like action=#{Profil.loadProductLines()}