Using kaptcha with JSF - jsf

I'm trying to use http://code.google.com/p/kaptcha/ which looks like a very easy way to include CAPTCHA. My demo app is JSF and although the instructions are simple for JSP, I don't know how to use them in JSF. How do I translate this in JSF?
In your code that manages the submit action:
String kaptchaExpected = (String)request.getSession()
.getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
String kaptchaReceived = request.getParameter("kaptcha");
if (kaptchaReceived == null || !kaptchaReceived.equalsIgnoreCase(kaptchaExpected))
{
setError("kaptcha", "Invalid validation code.");
}
I tried putting it in my:
public String button1_action() {
// TODO: Process the action.
return "success";
}
but it doesn't understand the request object :(

This equivalent JSF action should do it:
// bind to <h:inputText value="#{thisbean.kaptchaReceived}" />
private String kaptchaReceived;
public String getKaptchaReceived() {
return kaptchaReceived;
}
public void setKaptchaReceived(String kaptcha) {
kaptchaReceived = kaptcha;
}
public String button1_action() {
if (kaptchaReceived != null) {
FacesContext context = FacesContext
.getCurrentInstance();
ExternalContext ext = context.getExternalContext();
Map<String, Object> session = ext.getSessionMap();
String kaptchaExpected = session
.get(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
if (kaptchaReceived.equalsIgnoreCase(kaptchaExpected)) {
return "success";
}
}
return "problem";
}
This assumes that you want to use h:inputText and h:graphicImage in your JSF view instead of HTML elements.

Implementing validator is another easy way to validate the kaptcha.
<h:inputText id="kaptcha" autocomplete="off" required="true">
<f:validator validatorId="kaptchaValidator" />
</h:inputText>
<h:message for="kaptcha" styleClass="errorMessage"/>
--- Validator ---
public class KaptchaValidator implements Validator {
#Override
public void validate(FacesContext facesContext, UIComponent uiComponent, Object value) throws ValidatorException {
HttpSession session = (HttpSession) facesContext.getExternalContext().getSession(true);
String kaptchaExpected = (String) session.getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
String kaptchaReceived = (String) value;
if (kaptchaReceived == null || !kaptchaReceived.equalsIgnoreCase(kaptchaExpected)) {
FacesMessage message = new FacesMessage();
message.setDetail("Invalid Security Code.");
message.setSummary("Invalid security code.");
message.setSeverity(FacesMessage.SEVERITY_INFO);
throw new ValidatorException(message);
}
}

You can retrieve the request object from the JSF External Context, which is accessible from the FacesContext, using the following code:
HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
Edit (thanks to McDowell) :
Another way is to use the FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap() method to access the request parameters...

Related

How to make a dynamically generated URL redirect to another tab?

I'm using PrimeFaces 6.2
Hi everyone. As mentionned in the title, I need to open a new tab when a user clicks on a link (which is dynamically generated). I tried 2 solutions for now, and none of them works entirely :
1st solution : attributes url and target in PrimeFaces component
Facelet :
<p:contextMenu id="menuMesure" for="treeVArboParents" nodeType="3">
<p:menuitem value="OPL" url="#{arboParObjView.sessionService.lienUrl()}" target="_blank"/>
</p:contextMenu>
View :
#Named(value="arboParObjView")
#ViewScoped
public class ArboParObjView implements Serializable
{
#Inject
SessionService sessionService;
private TreeNode selectedNode //changes everytime a node is selected - both right and left clicks work
...some code here...
public void genererLienBirt() //called everytime the selectedNode value is changed
{
String libelle="";
if (selectedNode != null)
{
//code to find the id of the associated to the selected node.
//I need the id because I want to pass it as a parameter of the link
//And this part of code works well
sessionService.setIdMesure(idMesure);
}
}
}
Session Service :
#Named(value="sessionService")
#SessionScoped
public class SessionService implements Serializable
{
private LienURL lienUrl = new LienURL();
public String lienUrl()
{
String lien = "";
if (idMesure != null)
{
lien = lienUrl.getUrl();
lien += idMesure.toString();
return lien;
}
return "";
}
}
Bean :
public class LienURL
{
private String url;
public LienURL()
{
this.url = "myLink&BirtParameter="; //The base link with a Birt parameter waiting for the idMesure to be passed.
}
}
This solution doesn't work. When the user click on the menu item of the context menu component, it's opening a new tab but the opened page is the same as the one the user just leaved. I think that's because the PF's attribute url loads the url once (and the first time, my url is null because the idMesure isn't filled yet), and it just ignores the good link I try to pass after idMesure is filled.
2nd solution : use the redirect of the FacesContext
Facelet :
<p:contextMenu id="menuMesure" for="treeVArboParents" nodeType="3">
<p:menuitem value="OPL" actionListener="#{arboParObjView.sessionService.lienUrl()}" />
</p:contextMenu>
Service :
#Named(value="sessionService")
#SessionScoped
public class SessionService implements Serializable
{
private LienURL lienUrl = new LienURL();
public void lienUrl() throws IOException
{
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
String url = lienUrl.getUrl()+idMesure.toString();
ec.redirect(url);
}
}
The bean and the view don't change. It's the same as in the 1st solution.
The second solution works better than the first one. It is opening the good page with the good url, but in the same tab as the page where the user was. Is there a way to use the FacesContext redirect, but in another tab, as the target="_blank" do (the target only works with the url attribute) ? Or is there a way to make the url attribute read other urls than the first passed (which is null) ?
Thanks, and excuse my english.
Please use target="_blank" in p:menuitem only in second solution and it should work.
Below is updated code
<p:contextMenu id="menuMesure" for="treeVArboParents" nodeType="3">
<p:menuitem value="OPL" actionListener="#{arboParObjView.sessionService.lienUrl()}" target="_blank" />
</p:contextMenu>
and
public void lienUrl() throws IOException
{
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
String url = lienUrl.getUrl()+idMesure.toString();
ec.redirect(url);
}
Thanks to all the contributors for their help. Solution below :
View :
#Named(value="arboParObjView")
#ViewScoped
public class ArboParObjView implements Serializable
{
#Inject
private TreePodeService treePodeService;
private TreeNode selectedNode;
private Integer idMesure;
private String lienOplBirt;
...
//redirect to the generated link (called by the UI)
public void redirectOpl()
{
try {
FacesContext.getCurrentInstance().getExternalContext.redirect(lienOplBirt);
} catch (IOException e) {
e.printStackTrace();
}
}
//generate the Birt Link
public void genererLienBirt()
{
String libelle = "";
if (selectedNode != null)
{
libelle = selectedNode.getData().toString();
VArboParObjectifsParents mesureSelected = treePodeService.getPodeArboObjParentDao().findByLibelle(libelle);
idMesure = mesureSelected.getIdRoot();
}
lienOplBirt = "https://theLinkToPass"+"&RP_idMesure="+this.idMesure;
}
...
//Execute the genererLienBirt() method everytime selectedNode's value changes
public void setSelectedTreeNode(TreeNode selectedNode) {
if (selectedNode != this.selectedNode)
{
this.selectedNode = selectedNode;
genererLienBirt();
}
this.selectedNode = selectedNode;
}
}
Facelet (UI)
<p:menuitem value="OPL" includeViewParams="true" action="#{arboParObjView.redirectOpl()}" ajax="false" />

ValueChangeEvent although value has not changed

I cannot see why the question is duplicate. If I debug the code then - when the button is clicked - no new value of projectSelected is being detected. Even the hashCode is the same. The equals method of the ProjectEntity only contains the id which is the same since it comes from the database and is not changed anywhere. Null values don't exist in the selection.
There was, however, too much code to reproduce the problem. I removed unnecessary code and the problem still persists.
Original question: In the following form with 3 <p:selectOneMenu> -fields if the submit button is clicked a valueChangeEvent is fired for the projectSelector field although it hasn't changed. Why is that? Like that the actual action behind the button is never called. I would expect a valueChangeEvent to be fired only in case the project changes.
Update: Trying to find the cause I replaced the ProjectEntity with String and then it worked. So I thought it must be the equals method of ProjectEntity but that only compares the id. I debugged further and found out that the selected value is being compared with a ProjectEntity with all fields set to null which gives a false and hence a valueChangeEvent. So the question is why is there a ProjectEntity with all fields set to null? I debugged into UIInput.compareValues which has that "null"-ProjectEntity being the previous value. That is being returned by UIOuput.getLocalValue. Where does it come from?
Update2: Even when using the equals and hashCode from selectOneMenu shows after submit always the last item in the list as selected item the behaviour does not change. I created an ear file readily to be deployed to e.g. a wildfly and would appreciate any help since I am stuck on this question.
<h:form>
<p:outputLabel value="#{msgs.timeProject}"/>
<p:selectOneMenu value="#{timeBean.model.projectSelected}"
converter="projectConverter"
onchange="submit()"
valueChangeListener="#{timeBean.projectChanged}"
immediate="true"
required="true">
<f:selectItems value="#{timeBean.model.allProjects}"
var="singleProject"
itemValue="#{singleProject}"
itemLabel="#{singleProject.name}"/>
</p:selectOneMenu>
<p:commandButton value="#{msgs.send}"
action="#{timeBean.myAction}"
ajax="false"/>
<p:outputLabel value="#{timeBean.model.resultValue}"
rendered="#{not empty timeBean.model.resultValue}"/>
</h:form>
The converter
#FacesConverter(value = "projectConverter")
public class ProjectConverter implements Converter {
#Inject
private ProjectService projectService;
#Override
public Object getAsObject(final FacesContext facesContext, final UIComponent uiComponent, final String projectName) {
if (StringUtils.isEmpty(projectName)) {
return null;
}
final List<ProjectEntity> projects = projectService.findAll();
for (ProjectEntity project : projects) {
if (StringUtils.equals(projectName, project.getName())) {
return project;
}
}
return null;
}
#Override
public String getAsString(final FacesContext facesContext, final UIComponent uiComponent, final Object value) {
if (value == null) {
return null;
}
if (value instanceof ProjectEntity) {
return ((ProjectEntity) value).getName();
}
return "???projectName???";
}
}
The equals-method of the ProjectEntity
#Override
public boolean equals(final Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
final ProjectEntity that = (ProjectEntity) o;
return id != null ? id.equals(that.id) : that.id == null;
}
And the change listener inside the timeBean
public void projectChanged(final ValueChangeEvent event) {
final ProjectEntity projectSelected = (ProjectEntity) event.getNewValue();
model.setProjectSelected(projectSelected);
final FacesContext context = FacesContext.getCurrentInstance();
context.renderResponse();
}
The TimeModel
public class TimeModel {
private ProjectEntity projectSelected;
private List<ProjectEntity> allProjects;
private String resultValue;
... getters and setters ...
I'll guess, that the problem resides inside the ProjectConverter class, cause it may run into troubles to assign a valid projectService instance. Maybe you remove the injection and try to compute the value programatically in the getAsObject, getAsString methods by explicit cdi-finders.
I remember to run in a similar situation, when i was injecting in a ServletFilter.

How to get a UIComponent by its component id in icefaces

I'm trying to access an icefaces component, exactly an Accordion so i can set its activeIndex from my bean. the problem is that the returned value is always null. this is my code.
public static UIComponent findComponentInRoot(String id) {
UIComponent component = null;
FacesContext facesContext = FacesContext.getCurrentInstance();
if (facesContext != null) {
UIComponent root = facesContext.getViewRoot();
component = findComponent(root, id);
}
return component;
}
public static UIComponent findComponent(UIComponent base, String id) {
if (id.equals(base.getId()))
return base;
UIComponent kid = null;
UIComponent result = null;
Iterator kids = base.getFacetsAndChildren();
while (kids.hasNext() && (result == null)) {
kid = (UIComponent) kids.next();
if (id.equals(kid.getId())) {
result = kid;
break;
}
result = findComponent(kid, id);
if (result != null) {
break;
}
}
return result;
}
and i call this method like this:
Accordion acco = (Accordion)findComponentInRoot("menuFormId:menu");
my page look like this or to say a part of it:
<h:form id="menuFormId">
<icecore:singleSubmit />
<ace:accordion id="menu" collapsible="true" autoHeight="false" >
<ace:accordionPane id="system" title="#{msgs.LABEL_ADMINISTRATION}"
rendered="#{navigationCtrl.functionList['GESUTAD'] or navigationCtrl.functionList['GESPROF'] or navigationCtrl.functionList['GESUTTOM'] or navigationCtrl.functionList['SYNCPRC']}">
<div class="divLinkStyle">
<ice:commandLink rendered="#{navigationCtrl.functionList['GESPROF']}" styleClass="linkMenu" action="#{navigationCtrl.redirectConsulterProfil}"
onmouseover="this.style.backgroundColor='#DEEDF8'" onmouseout="this.style.backgroundColor='#FFFFFF'">
<h:graphicImage value="../resources/images/util.png" />
<h:outputLabel value="#{msgs.LABEL_GESTION_PROFIL}" style="cursor: pointer;" />
</ice:commandLink>
</div>
...
Any ideas ?
my bean is session scoped.
i'm using icefaces 3.3.0 and jsf 2.2
You're confusing component ID with client ID. You're passing a client ID "menuFormId:menu" instead of component ID "menu" to your utility method, while the utility method actually finds the component by component ID instead of client ID.
Just use UIViewRoot#findComponent().
public static UIComponent findComponentInRoot(String id) {
return FacesContext.getCurrentInstance().getViewRoot().findComponent(id);
}
Unrelated to the concrete problem. You're making here a design mistake. The model should not be interested in the view. It should be the other way round. Set the activeIndex as a bean property and let the view hook on it the usual way.
<ace:accordion ... activeIndex="#{bean.activeIndex}">
In any case you're trying to grab/create/bind/manipulate/whatever a physical UIComponent instance in a backing bean class, you should absolutely stop coding and think twice if you're really doing things the right way. Ask if necessary at Stack Overflow if you can't figure out the right way.

Error in streaming dynamic resource. Using p:graphicImage with Primefaces

I'm trying to dynamically display an image in primefaces using the p:graphicImage tag as follows:
<p:graphicImage value="#{submissionBean.contestImage}">
<f:param name="imageName"
value="#{contestBean.createContest.submissions[0].fileName}" />
</p:graphicImage>`
The managed bean is as follows:
#ManagedProperty("#{param.imageName}")
private String imageName;
public String getImageName()
{
return imageName;
}
public void setImageName(String imageName)
{
this.imageName = imageName;
}
private StreamedContent contestImage;
public StreamedContent getContestImage()
{
FacesContext context = FacesContext.getCurrentInstance();
if (imageName == null)
imageName = Constants.SUBMISSION_FILE_DIR + "/" + "sacxzx_asdsdaas_icon.png";
if (context.getRenderResponse())
{
// So, we're rendering the view. Return a stub StreamedContent so
// that it will generate right URL.
return new DefaultStreamedContent();
}
else
{
return new DefaultStreamedContent(this.getClass().getResourceAsStream(Constants.SUBMISSION_FILE_DIR + "/" + imageName));
}
}
I'm always getting the error of "SEVERE: Error in streaming dynamic resource."
Checking the URL for the image seems just fine:
http://localhost:8080/mashup/javax.faces.resource/dynamiccontent.xhtml?ln=primefaces&pfdrid=pfdrid_4290aa0c-8eef-45ea-a281-638e460e33bf&imageName=sacxzx_asdsdaas_icon.png
Any idea why this is?
Thanks!
Should be SessionScoped. As method getContestImage() is called multiple times during page processing, it is better to create the stream only once.

Programmatically get expression value of facelets parameter (variable)

Following java code allows to access any object or variable from faces context:
ELContext elCtx = facesContext.getELContext();
ExpressionFactory exprFac = facesContext.getApplication().getExpressionFactory();
MyProperty myProperty = (MyProperty) exprFac.createValueExpression(elCtx, "#{somebean.someattr.someproperty}", MyProperty.class).getValue(elCtx);
I use the code from within my custom converter to read additional converting parameters from context.
The code works correctly if #{somebean} is defined as normal backing bean within JSF context.
Facelets allow to create 'shortcut' to JSF expressions. Example:
<ui:param name="shortcut" value="#{somebean.someattr.someproperty}" />
<div>#{somebean.someattr.someproperty} equals #{shortcut}</div>
In this case both #{somebean.someattr.someproperty} and #{shortcut} have the same value.
However these 'shortcut' names are not accessible using java code above. For example:
MyProperty myProperty1 = (MyProperty) exprFac.createValueExpression(elCtx, "#{somebean.someattr.someproperty}", MyProperty.class).getValue(elCtx);
// myProperty1 has expected value
MyProperty myProperty2 = (MyProperty) exprFac.createValueExpression(elCtx, "#{shortcut}", MyProperty.class).getValue(elCtx);
// myProperty2 is null
Is there a way to access a facelets context and to read 'shortcut' parameter values, defined on the current JSF page?
I had the same problem and have chosen the following approach:
/**
* Führt eine Expression im aktuellen Faces EL Context
* UND im Facelets El Context aus.
*
* #param facesContext
* #param expression
* #return object
*/
private static Object executeExpressionInUIContext (final FacesContext facesContext, final String expression) {
final ELContext elContext = facesContext.getELContext();
final Application application = facesContext.getApplication();
Object result = executeExpressionInElContext(application, elContext, expression);
if (null == result) {
FaceletContext faceletElContext = (FaceletContext) FacesContext.getCurrentInstance().getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY);
result = executeExpressionInElContext(application, faceletElContext, expression);
}
return result;
}
private static Object executeExpressionInElContext (Application application, ELContext elContext, String expression) {
ExpressionFactory expressionFactory = application.getExpressionFactory();
ValueExpression exp = expressionFactory.createValueExpression(elContext, expression, Object.class);
return exp.getValue(elContext);
}
"ui:param" is part of the Facelet view handling technology. Facelets extends JSF.
Both technologies use their own Context when storing variables.
Beside the Faces El Context there is a Facelet El Context (FaceletContext).
The stated method evaluates expressions in both contexts. Be aware that this will not work if two values are stored under the same name in each context.
It seems that facelet shortcuts do not exist in the context, where I try to access them.
I have made following workaround: On JSF page where my input element is placed, I have added a <f:param> element as child of the input with my converter.
<h:inputText id="myid" value="#{action.myinput}">
<f:converter converterId="myConverter" />
<f:param name="converterParameters" shortcut="#{somebean.someattr.someproperty}"/>
</h:inputText>
Then in converter I'm able to find UIParam element as one of the input children and read my shortcuts from it.
public Object getAsObject(FacesContext context, UIComponent component, String value) {
MyProperty myProperty = null;
try {
for (final UIComponent child : component.getChildren()) {
if ("converterParameters".equals(child.getAttributes().get("name"))) {
final ELContext elCtx = context.getELContext();
myProperty = (MyProperty) child.getValueExpression("shortcut").getValue(elCtx);
break;
}
}
if (myProperty == null) {
throw new NullPointerException("My property is undefined.");
}
} catch (Exception e) {
LOG.error("Cannot convert " + value + ". Use <f:param name=\"converterParameters\" "
+ "shortcut=\"#{here.comes.shortcut}\"/> for your input element. ", e);
throw new ConverterException("Cannot initialize converter.", e);
}
//...
}
The mapping of ui:param is not stored in context, it's in the VariableMapper of each individual ValueExpression.
So if you need to create ValueExpression programmatically, relying on another ValueExpression's varMapper, you can do something like this:
VariableMapper varMapper = new DefaultVariableMapper();
varMapper.setVariable(mappingName, component.getValueExpression(mappedAttributeName));
return new ValueExpressionImpl(expression, null, null, varMapper, expectedType);

Resources