how to use a seam validator in jsf? - java

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.

Related

JSF selectOneMenu converter null or class cast exception

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.

NPE in PrimeFaces validator attribute method

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?

JSF Email Validation - Do not allow domains like yahoo, gmail, etc to be accepted

I already have an email validator that works alright to check format of emails in the form of regular expression.
Now - I want to write another email validator in Java (since my application is on JSF, Glassfish 3.x) that does not allow non-government emails to be entered in the textbox. All domains like yahoo, gmail, hotmail etc. should not be accepted.
It should only accept Indian government domain based emails - #gov.in, #nic.in - all other emails should be rejected and the user should be suitably informed.
Requesting help in this regard. Thanks in advance.
I make e-mail validator based on the especification:
EmailValidator.java
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.FacesValidator;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;
#FacesValidator("com.lucio.EmailValidator")
public class EmailValidator implements Validator {
private static final String EMAIL_PATTERN = "^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*#(gov|nic)(\\.in)$";
private Pattern pattern;
private Matcher matcher;
public EmailValidator(){
pattern = Pattern.compile(EMAIL_PATTERN);
}
#Override
public void validate(FacesContext context, UIComponent component,
Object value) throws ValidatorException {
matcher = pattern.matcher(value.toString());
if(!matcher.matches()){
FacesMessage msg =
new FacesMessage("E-mail validation failed.",
"Invalid E-mail acepted only #gov.in or #nic.in");
msg.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(msg);
}
}
}
For use:
<h:inputText id="email" value="#{user.email}"
label="Email Address">
<f:validator validatorId="com.lucio.EmailValidator" />
</h:inputText>

spring - How can I pass a generic (parameter,value)-couple from my login controller to my authentication provider?

What I want to do is read a http-parameter on my login page, e.g. login.html?param=value, and then pass value to my AuthenticationProvider. My idea was to somehow save value in a hidden parameter, but I still don't know how to pass it on.
Is this possible? How do I go about to do it?
Edit: Following Sanath's advice and after doing some reading I was finally able to solve the problem -- look below if you're interested in how I did it.
I did the following and finally it worked like a charm.
In my bean configuration file I had to enter:
<http auto-config="true>
...
<form-login ... authentication-details-source-ref="myWebAuthDetSource"/>
...
</http>
<beans:bean id="myWebAuthDetSource" class="com.my.pack.age.MyWebAuthDetSource"/>
...
then I've set up my WebAuthenticationDetailsSource implementation like this:
public class MyWebAuthDetSource implements AuthenticationDetailsSource<HttpServletRequest, MyWebAuthDets> {
public MyWebAuthDetSource buildDetails (HttpServletRequest context) {
return new MyWebAuthDets(context);
}
}
then I've got my AuthenticationDetails implementation like:
public class MyWebAuthDets extends WebAuthenticationDetails {
private final String parameter;
public MyWebAuthDets(HttpServletRequest request) {
super(request);
this.parameter = request.getParameter("paramId");
}
}
And then I overlook the most simple fact, that the authentication process processes only what it gets on submit from the login form, so I simply had to add the following in login.jsp:
...
<form id="j_spring_security_check" ... >
<input type="hidden" name="paramID" value="${param['paramID']}" />
...
</form>
...
And that was all. Now I can add an authenticationProvider and get my parameter with .getDetails() from the authenticationToken.
Thanks again #Sanath.

Seam - ListSelectMenus with dependencies

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()}

Categories

Resources