I use a facelets login form for Spring Security:
<h:messages globalOnly="true" layout="table" />
<h:form id="formLogin" prependId="false">
<h:outputLabel for="j_username" value="Usuario:" />
<h:inputText id="j_username" value="#{autenticacionController.administrador.login}" />
<h:outputLabel for="j_password" value="ContraseƱa:" />
<h:inputSecret id="j_password" value="#{autenticacionController.administrador.password}" />
<h:commandButton value="Entrar" action="#{autenticacionController.loginAction}" />
<h:commandButton value="Cancelar" immediate="true" action="#{autenticacionController.cancelarAction}" />
</h:form>`
The loginAction method forwards the request with this:
FacesContext.getCurrentInstance().getExternalContext().dispatch("/j_spring_security_check")
It works fine, but how can I show a facesmessage in my h:messages tag if the BadCredentials exception is thrown by Spring Security?
I know it can be done with a phase listener, but I don't like that way (dealing with exceptions in listeners).
I'm trying another way, configuring Spring Security like this:
authentication-failure-url="/faces/paginas/autenticacion/login.xhtml?error=1
And then in the login page, catch the GET param "error". But how can I show the facesmessage in this way?
Another way I tried was to override the messages properties file of Spring Security (overriding the message for the key "badcredentials"), but it didn't work neither (I didn't know how to show the message).
Anyone know how to do it?
Thank you very much in advance.
And then in the login page, catch the GET param "error". But how can I show the facesmessage in this way?
This way:
<f:metadata>
<f:viewParam name="error" validator="#{auth.checkErrors}" />
</f:metadata>
<h:messages />
with
public void checkErrors(FacesContext context, UIComponent component, Object value) {
if ("1".equals(value)) {
throw new ValidatorException(new FacesMessage("Invalid credentials"));
}
}
or maybe so:
<f:metadata>
<f:viewParam name="error" value="#{auth.error}" />
<f:event type="preRenderView" listener="#{auth.checkErrors}" />
</f:metadata>
<h:messages />
with
private int error;
public void checkErrors() {
if (error == 1) {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Invalid credentials"));
}
}
Either way, this feels pretty hacky :)
Related
So I made a composite component FileAdder.xhtml
<composite:interface>
<composite:attribute name="type" value="#{editoriCompositeController.typeString}"/>
</composite:interface>
<composite:implementation>
<h:form>
<p:editor id="editor" widgetVar="editorWidget" value="some text" width="600" />
</h:form>
</composite:implementation>
And then I have the EditoriCompositeController ManagedBean:
#ViewScoped
#ManagedBean
public class EditoriCompositeController {
String typeString;
public void setTypeString(String typeStringParameter) {
this.typeString = typeStringParameter;
}
public String getTypeString() {
return typeString;
}
}
And then in my fileattachmentsview.xhtml I use the component:
<owncomponents:fileadder type="MEMO" />
But that is not setting the typeString value in the backing bean as "MEMO". It remains as null I tested it with a button that prints the value.
How can I make the backing bean get the value for typeString I set to the composite component's type-attribute as "MEMO"? Why it's null and not "MEMO"?
You have to pass the target bean/model as another composite attribute. Then you can inside the composite use <c:set> to set a property on it.
<cc:interface>
<cc:attribute name="bean" type="com.example.Bean" />
<cc:attribute name="type" type="java.lang.String" />
</cc:interface>
<cc:implementation>
<c:set target="#{cc.attrs.bean}" property="type" value="#{cc.attrs.type}" />
<p:editor value="#{cc.attrs.bean.text}" />
</cc:implementation>
Usage:
public class Bean {
private String text;
private String type; // I suggest to make it an enum.
// ...
}
<h:form>
<your:composite bean="#{bean}" type="MEMO" />
<p:commandButton action="#{bean.submit}" />
</h:form>
Note that I factored the form outside the composite. Having a form inside a composite is poor practice.
See also:
When to use <ui:include>, tag files, composite components and/or custom components?
I solved it by manually getting the "type" attribute from the component in the backing bean by:
String typeString = (String) component.getAttributes().get("type");
How can I create an "edit" button so that when the button is clicked it will change the h:outputText to h:inputText?
Make use of the rendered attribute:
<h:outputText value="#{bean.entity.property}" rendered="#{not bean.editmode}" />
<h:inputText value="#{bean.entity.property}" rendered="#{bean.editmode}" />
...
<h:commandButton value="Edit" action="#{bean.edit}" rendered="#{not bean.editmode}" />
<h:commandButton value="Save" action="#{bean.save}" rendered="#{bean.editmode}" />
With this in a view scoped bean:
private boolean editmode;
public void edit() {
editmode = true;
}
public void save() {
entityService.save(entity);
editmode = false;
}
public boolean isEditmode() {
return editmode;
}
// ...
Note that the bean being view scoped is important for the reason mentioned in point 5 of this answer: commandButton/commandLink/ajax action/listener method not invoked or input value not updated.
Alternatively, you can use the disabled attribute on input component in combination with a shot of CSS which basically makes it look like an output component (by removing the border).
<h:inputText value="#{bean.entity.property}" disabled="#{not bean.editmode}" />
...
<h:commandButton value="Edit" action="#{bean.edit}" rendered="#{not bean.editmode}" />
<h:commandButton value="Save" action="#{bean.save}" rendered="#{bean.editmode}" />
with e.g.
input[disabled] {
border: 0;
}
Also here, the bean must be view scoped. See also How to choose the right bean scope?
I am using Primefaces 4.0 and JSF 2.2. When I make a DataTable with row Edit and I set a valdiationFailed() on the rowEdit event, the roweditor is closing, which I want to prevent.
I added an oncomplete js function like:
<p:ajax event="rowEdit" listener="#{customerUI.onInvoiceRowEdit}"
oncomplete="if(!args.validationFailed) {updateTable();}" update=":messages" />
My remote command is as follows:
<p:remoteCommand name="updateTable" update=":form:addressTabs:customerTable" />
So this keeps the editor when the validation fails, but now the editors accept and cancel buttons doesn't work, so does editing other things on the side until i do a manual refresh.
I just want the editor to stay when the validation fails and to correct the input, if the validation went good, the editor can be closed.
Anyone any solution to this?
So I had a tangentially related problem and this question helped me figure out a solution so I thought I should share it here in case someone else lands on this question with a similar issue and needs help.
So I had a dataTable and a rowEdit ajax call to a function when a cell within the row is edited, I also used a remoteCommand to update the dataTable after I make some changes in the DB. Problem was if the validation for the cell edit value failed the remoteCommand would refresh the form and the error messages would disappear before they could be read. I wanted the ajax call on rowEdit to only go through if there were no validation errors, otherwise I wanted to display the error message with the row editor still open. So I used the args.validationFailed parameter as shown in the question to prevent the remoteCommand from being executed in case the custom validator fails. The following is a basic example of what I did.
JSF page:-
<h:form id="form_name">
<p:remoteCommand name="refreshForm" update="#form" />
<p:dataTable id="table_id" var="varTableData"
value="#{bean.tablerows}"
editable="true">
<p:ajax event="rowEdit"
listener="#{bean.onRowEdit}"
oncomplete="if(!args.validationFailed) refreshForm()" />
<p:column>
<f:facet name="header">
<h:outputText value="Column Title" />
</f:facet>
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{varTableData.someValue}" />
</f:facet>
<f:facet name="input">
<p:inputText id="cellId"
value="#{varTableData.someValue}"
label="Some Label" required="true" style="width:100%"
validator="#{bean.validationMethod}">
</p:inputText>
<p:message id="someMsg" for="cellId" />
</f:facet>
</p:cellEditor>
</p:column>
</p:dataTable>
</h:form>
Custom Validation Method :-
public void validationMethod(FacesContext context, UIComponent comp, Object value) {
int someCellValue = (int) value;
if (someCellValue < 6) {
((UIInput) comp).setValid(false);
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR,
"Some error message",
"some error message");
context.addMessage(comp.getClientId(context), message);
}
}
I know this is a very common issue which happens for a variety of reasons, but although I have searched about it a lot, I didn't manage to find a solution for my case.
My primefaces commandButton is inside an h:form and is declared as follows:
<p:commandButton action="#{userGroupBean.createOrUpdateItemAction}"
value="#{userGroupBean.actionCreateOrUpdateLabel}"
icon="iconDisk"
update="#form"
oncomplete="window.scrollTo(0,0);" />
My UserGroupBean is ViewScoped.
This button when clicked is supposed to create a userGroup and show a "Successful creation" message. Instead of doing this it just shows "Please wait" loader for a second and then does nothing. There are no errors in the log and through remote debugging I confirmed that it doesn't enter the action method.
The weird thing is that when I run the same code in a local Tomcat installation it runs successfully. Also, on the remote server this application is deployed, this used to work just fine. All happened suddenly, and I have a lot of commandButtons like this one, across my application which still work great. Something seems to go wrong just with this particular page .
I use PrimeFaces 3.1 version, I dont know what other information is usefull to provide.
Any help/ideas is/are appreciated.
EDIT
This is userGroup.xhtml (the page whose buttons do not work) code:
<ui:composition
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:fn="http://java.sun.com/jsp/jstl/functions"
xmlns:comps="http://java.sun.com/jsf/composite/components"
template="/WEB-INF/templates/userGroup/edit.xhtml">
<f:metadata>
<f:event type="preRenderView" listener="#{userGroupBean.initUserGroupUsersList}" />
</f:metadata>
<ui:define name="title">
User Group
</ui:define>
<ui:define name="centerUnit">
<h:form>
<h:panelGrid columns="3" styleClass="ui-messages-info ui-corner-all" rendered="#{not empty flash.messages_info}">
<span class="ui-message-info-icon"></span>
#{flash.messages_info}
</h:panelGrid>
<p:messages showDetail="true" globalOnly="true" />
<p:panel header="Settings">
<h:panelGrid columns="3" cellpadding="5">
<h:outputLabel for="title" value="User Group Title:" />
<p:inputText id="title" value="#{userGroupBean.userGroup.title}" />
<p:message for="title" />
</h:panelGrid>
</p:panel>
<p:panel header="Members">
<p:pickList value="#{userGroupBean.usersGroupUsersList}" var="user"
iconOnly="true"
converter="userConverter"
itemLabel="#{user.username}" itemValue="#{user}">
<f:facet name="sourceCaption">Available</f:facet>
<f:facet name="targetCaption">Participating</f:facet>
<p:column style="width:25%">
<p:graphicImage value="#{facesContext.externalContext.request.contextPath}/../file?thumbnail=&downloadPath=#{user.contactDetails.picture.downloadPath}" width="40" height="40" />
</p:column>
<p:column style="width:25%">
<h:outputText value="#{user.username}" />
</p:column>
<p:column style="width: 50%">
<h:outputText value="#{user.contactDetails.lastName} #{user.contactDetails.firstName}"/>
</p:column>
</p:pickList>
</p:panel>
<p:commandButton
action="#{userGroupBean.createOrUpdateItemAction}"
value="#{userGroupBean.actionCreateOrUpdateLabel}"
icon="iconDisk"
update="#form"
oncomplete="window.scrollTo(0,0);" />
<p:separator />
<p:outputPanel style="text-align:right" layout="block" rendered="#{!userGroupBean.userGroup.isNew() and userGroupBean.hasUserModifyUserGroupAuthority()}">
<h:panelGroup>
<p:commandButton value="Delete" title="Do you want to delete this user group?"
action="#{userGroupBean.deleteItemAction}"
update="#form"
icon="iconDelete" />
<p:commandButton value="Leave user group" title="I want to leave this user group"
action="#{userGroupBean.userInSessionLeavesUserGroupAction}"
update="#form"
icon="iconUserGo"/>
</h:panelGroup>
</p:outputPanel>
</h:form>
</ui:define>
The UserGroupBean.java:
#Controller("userGroupBean")
#Scope(value = "view")
public class UserGroupBean extends GenericBean<UserGroup> {
private static final long serialVersionUID = 1L;
final Log logger = LogFactory.getLog(getClass());
#Autowired
private SessionServiceImpl sessionService;
#Autowired
protected UserGroupService userGroupService;
#Autowired
protected UserService userService;
#Override
public String getPageCreateOrUpdate() { return "userGroup.xhtml"; }
#Override
public String getPageList() { return "../../my/userGroups.xhtml"; }
/** Wrapper method which calls getItem() */
public UserGroup getUserGroup() { return getItem(); }
/** Wrapper method which calls setItem() */
public void setUserGroup(UserGroup UserGroup) { setItem(UserGroup); }
protected UserGroup findItemById(Integer userGroupId) {
return userGroupService.findUserGroupById(userGroupId);
}
#Override
protected void resolveCreateRequest(HttpServletRequest req) {
logger.debug("UserGroupBean::resolveCreateRequest()");
setItem( userGroupService.initializeUserGroup("") );
}
#Override
protected String createItem() {
try {
if(!isValidUsersSelection(new ArrayList<User>(usersGroupUsersList.getTarget())))
return null;
List<Integer> userIds = new ArrayList<Integer>();
for(User user: usersGroupUsersList.getTarget())
userIds.add(user.getId());
userGroupService.saveUserGroup(getUserGroup(), userIds);
helperFacesContext.addInfoMessageToFlash("User Group successfully created.");
return getPageCreateOrUpdate()+"?action=update&id="+getItem().getId()+"&faces-redirect=true";
}
catch (Exception e) {
JsfUtils.error("There was an error", e.getMessage());
return null;
}
}
.....
}
And finally the UservConverter.java
#FacesConverter(value = "userConverter")
public class UserConverter extends GenericConverter {
final Log logger = LogFactory.getLog(getClass());
private UserDAO getUserDAO(FacesContext facesContext) {
// #Autowired gives null
return (UserDAO) FacesContextUtils.getWebApplicationContext(facesContext).getBean("userDAO");
}
/**
* converts the String representation of the key back to the Object
*/
#Override
public Object getAsObject(FacesContext context, UIComponent component,
String value) throws ConverterException {
logger.info("UserConverter::getAsObject("+value+")");
Integer userId = Integer.valueOf(value);
try {
return getUserDAO(context).findById(userId);
} catch(Exception e) {
throw new ConverterException(handleException(context,e.getMessage()));
}
}
NEW CLUE: When I restart the server and only after that, when I access this page and manage to create a user group before userGroup.xhtml is fully loaded, the user group is created just fine. If I restart the server and wait till the page is loaded and then create a user group again nothing happens. This problem will drive me crazy at the end.
I am doing a product insert form in PrimeFaces dialog. My bean's scope is session. I can insert a product, but when I try to insert another product, the form has previous product's information. I want to reset form. I tried UIInput but it doesnt work. How can I clear the form?
Just create a new product after saving it and make sure that you're ajax-updating the form after save.
E.g.
<h:form>
<h:inputText value="#{productController.product.name}" />
<h:inputTextarea value="#{productController.product.description}" />
<h:selectOneMenu value="#{productController.product.category}">
<f:selectItems value="#{applicationData.categories}" />
</h:selectOneMenu>
<p:commandButton value="Save" action="#{productController.save}" update="#form" />
</h:form>
with
public void save() {
productService.save(product);
product = new Product();
}
By the way, that kind of bean really doesn't belong in the session scope. Put it in the view scope.