JSF 2 : How handle exceptions in controller? - java

I would like to do a register form which works fine:
<h:form id="register_form">
<h:panelGrid columns="3">
<h:outputLabel for="email" value="E-mail:" />
<h:inputText id="email" value="#{userc.userb.user.email}">
<f:ajax event="blur" render="m_email" />
</h:inputText>
<h:message id="m_email" for="email" ajaxRendered="false" />
<h:outputLabel for="password" value="Passoword" />
<h:inputSecret id="password" value="#{userc.userb.user.password}">
<f:validator validatorId="confirmPasswordValidator" />
<f:attribute name="confirm" value="#{confirmPassword.submittedValue}" />
</h:inputSecret>
<h:message id="m_password" for="password" />
<h:commandButton value="Registerr" action="#{userc.register}">
<f:ajax execute="#form" render="#form" />
</h:commandButton>
<h:messages globalOnly="true" layout="table" />
</h:panelGrid>
</h:form>
And this is my controller:
public void register(){
FacesMessage message;
try {
userb.encryptPassword();
userEAO.create(userb.getUser());
message = new FacesMessage("Register successfully!");
FacesContext.getCurrentInstance().addMessage(null, message);
} catch (Exception e) {
// 1062 : duplicate entry
if (e.getMessage().contains("Duplicate")){
message = new FacesMessage("User already registered, please try another email");
FacesContext.getCurrentInstance().addMessage(null, message);
}else{
message = new FacesMessage("An error occured, try again please");
FacesContext.getCurrentInstance().addMessage(null, message);
}
}
}
So, for example, if some user try to register himself twice, I return this message, saying that's duplicate, but I wonder if this is the best approach.
How to treat these kinds of exceptions?

Those validations should be handled by your Business Logic Layer object, in this case, it looks like it is the userEAO object. You can decide if you should handle this error messages in the flavor of Exceptions (which IMHO won't use).
In my case, I prefer to use a Message class that basically contains an integer code and a message holding the results of the operation. For my case, I usually return 0 or more to indicate the operation was successful, and use negative codes to declare errors. This is a pretty basic example:
public class Message {
private int code;
private String message;
//constructor...
//getters and setters...
}
public class UserBL {
public Message create(User user) {
//do some fancy validations
if (!verifyDuplicatedUser(user)) {
return new Message(-1000, "Duplicated user");
}
//and the operations...
saveUser(user);
return new Message(0, "Register successfully!");
}
}
Now your controller will be forward to what it must really do (this means, no heavy business logic validations):
public void register() {
FacesMessage fmessage;
userb.encryptPassword();
Message message = userEAO.create(userb.getUser());
fmessage = new FacesMessage(message.getMessage());
FacesContext.getCurrentInstance().addMessage(null, fmessage);
}
This approach can even help you when you should validate a set of data, like validating the results of an Excel sheet and you must show a list of errors (because showing a single error lot of times is very annoying!).
You can improve the way you create the messages, like retrieving them from a table with the messages for your system, or using properties files to handling internationalization (that depends on your functional requirements, of course).

Related

Primefaces-Refreshing drop down list

I have a popup in which you have to select components for you pc. Each component contains a label and a drop down list with all the selected components in the database. Next to the list there is an 'add' button, when clicked it give another popup, which is used when the user does not find what he wants in the list. The add function calls a method which checks the input of the user for already existing inputs and adds if there is none. MY PROBLEM: is that I can not find a solution for refreshing the list which was updated. I am trying to use <p:ajax /> but I keep getting Cannot find component errors.
---HTML CODE----
<form id="dialog">
<h:outputText value="Computer: " styleClass="dialog-labels" />
<ul style="list-style-type: none">
<li>
<div class="dialog-container">
<h:outputLabel value="Processor: " style="float:left;" />
<p:selectOneMenu id="procs" filterFunction="true" filterMatchMode="true" styleClass="dialog-dropdown-list" style="width:15em;">
<f:selectItem itemLabel="Select Processor" itemValue="" noSelectionOption="true" />
<f:selectItems id="list" value="#{javaHTMLConnection.procList}" />
</p:selectOneMenu>
<p:commandButton value="Add" id="addProc" styleClass="dialog-buttons" onclick="PF('addProcBox').show();" style="font-size: 10px;" />
<h:form id="popUp">
<p:dialog header="Add Processor" widgetVar="addProcBox" height="200" width="180" draggable="false" resizable="false" style="font-size:13px;">
<h:outputLabel value="Processor: " style="float:left;" />
<p:inputText id="procInput" value="#{components.procID}"/>
<p:growl id="growl" life="10000" />
<p:commandButton value="Save" styleClass="dialog-bottom-buttons" action="#{components.addProcessor()}" update="growl" onclick="PF('addProcBox').hide();" style="font-size: 10px;" />
<p:ajax listener="#{javaHTMLConnection.onAdd()}" update=":dialog:list" />
<p:commandButton value="Cancel" id="CancelAddProc" styleClass="dialog-bottom-buttons" onclick="PF('addProcBox').hide();" style="font-size: 10px;" />
</p:dialog>
</h:form>
</div>
</li>
</form>
----JavaHTMLConnection----
public void onAdd()
{
if(components.addProcessor()==true)
{
components.getAllComponents();
}
}
----Components(Java code)----
public boolean addProcessor()
{
try
{
db.openDatabase();
db.con.setAutoCommit(false);
if (!db.ifExists("processor.name", "processor", procID))
{
db.Entry("processor", procID);
addMessage("Success ! Your input has been saved");
return true;
}
else
{
addMessage("Error, the input already exists");
}
}
catch (SQLException e)
{
try
{
db.con.rollback();
}
catch (SQLException e1)
{
e1.printStackTrace();
}
e.printStackTrace();
}
finally
{
try
{
db.con.setAutoCommit(true);
}
catch (SQLException e)
{
e.printStackTrace();
}
}
return false;
}
P.S.: Don't mind the naming as when this will be figured out everything will be made generic as I need to adapt this to 15 more components
As Emil already mentioned, nested forms are a really bad thing which you should get rid of immediately.
Also, as Emil suggested, that kind of error message is usually related to incorrect component-selectors. Those are mainly caused due to the fact that a parent naming container is missing in the selector. Either determine the correct id using some debugging tool or, instead of
<p:ajax listener="#{javaHTMLConnection.onAdd()}" update=":dialog:list" />
try using some of the solutions suggested in this thread: Primefaces - Cannot find component with identifier outside the datatable
I personally prefer to use the following syntax, which relies on a primefaces-method searching for an id in various naming containers:
<p:ajax listener="#{javaHTMLConnection.onAdd()}" update=":#{p:component('list')}" />
First of all, you have nested forms, which you should get rid of.
As for question you should inspect the specific html element (for example by using firebug) to find out the id of it.

JSF: data not showing when editing twice

I have a dataTable with a list of items and a checkbox for selecting an item to edit. Ticking an item and clicking the edit button pops up a component which has fields and update and cancel buttons. Here's what happens.
Dialog appears
I empty all fields and click UPDATE, required messages appear since all fields are empty, data is not saved
Click CANCEL, dialog disappears
Click and edit the same item again
Some fields are not showing. I checked the datatable and database, data for the item still exists. Just not showing on the edit dialog the 2nd time around.
I noticed that the fields not showing are only those that have NULL attributes. NOT NULL fields are fine. I wonder if this has something to do with sessions. (Using Primefaces for all components)
Code for the edit dialog
<p:dialog header="#{bundle.Edit}" modal="true" widgetVar="editDialog" resizable="false">
<h:form id="edit-form">
<p:messages id="edit-error" autoUpdate="true" closable="true"/>
<h:outputLabel value="#{bundle.Name}" for="name" /><span class="required">*</span>
<p:inputText id="name" value="#{controller.selected.name}" required="true" requiredMessage="#{bundle.Name} #{bundle.FieldIsRequired}" maxlength="45"/>
<h:outputLabel value="#{bundle.Input}" for="input" /><span class="required">*</span>
<h:selectOneRadio id="input" value="#{controller.selected.input}" required="true" requiredMessage="#{bundle.Input} #{bundle.FieldIsRequired}">
<f:selectItem itemLabel="◯ " itemValue="0" />
<f:selectItem itemLabel="☓ " itemValue="1" />
</h:selectOneRadio>
<h:outputLabel value="#{bundle.IsOption}" for="isOption" /><span class="required">*</span>
<h:selectOneRadio id="isOption" value="#{controller.selected.isOption}" required="true" requiredMessage="#{bundle.IsOption} #{bundle.FieldIsRequired}">
<f:selectItem itemLabel="◯ " itemValue="0" />
<f:selectItem itemLabel="☓ " itemValue="1" />
</h:selectOneRadio>
<h:outputLabel value="#{bundle.Remark}" for="remark" />
<p:inputTextarea id="remark" value="#{controller.selected.remark}"/>
<p:commandButton action="#{controller.update()}"
value="#{bundle.Save}"
actionListener="#{controller.prepareList()}"
oncomplete="handleEditDialog(xhr, status, args)"
update=":form:datatable :edit-form:edit-error"
/>
<p:commandButton value="#{bundle.Cancel}"
onclick="editDialog.hide(); reset();"
type="button"/>
</h:form>
</p:dialog>
Code for the update function
public String update() {
RequestContext context = RequestContext.getCurrentInstance();
try {
current.setUpdateDate(new Date());
Map<String, Object> param = JsfUtil.getExternal().getSessionMap();
int createUser = (Integer) param.get("LOGIN_ID");
Account account = accountFacade.find(createUser);
current.setUpdateUser(account);
getFacade().edit(current);
search();
prepareList();
JsfUtil.addSuccessMessage(ResourceBundle.getBundle(JsfUtil.getSessionBundle()).getString("Updated"));
updateOk = true;
current = null;
context.addCallbackParam("updated", true);
return "";
} catch (Exception e) {
if (e.getCause().getCause().getMessage().contains("uk_")) {
JsfUtil.addErrorMessage("edit-form:edit-error",JsfUtil.getResourceString("Duplicate"));
context.addCallbackParam("updated", false);
} else {
JsfUtil.addErrorMessage(e, ResourceBundle.getBundle(JsfUtil.getSessionBundle()).getString("PersistenceErrorOccured"));
context.addCallbackParam("updated", false);
}
return null;
}
}
Based on what you are describing I think the following scenario is happening. When you see the form and you delete all fields and you hit save, the components that have no validators are actually overwriting some of the previous property values of your bean. That's why when you reload it for a second edit, you'll notice that all the components that have a validator attached (e.g required = true) are appearing but those with no validators are blank. In reality, you are actually doing a partial update. What you actually need to do is mark all fields as required to avoid that problem.
My teammate just found out a fix for this one. There is a function on the controller generated by NetBeans called prepareList().
public String prepareList() {
recreateModel();
return "";
}
private void recreateModel() {
items = null;
}
Basically we added this on catch block of the update() function. It is called after every unsuccessful update and recreates the model. The bug existed because the field retained its state of not having a value after an unsuccessful update.

jsf rendered condition not working with selectOneMenu

Hi I have read tons of similar questions but the answers didnt work for me.
I have this
<p:selectOneMenu id="tipoTaxon" value="#{taxonDM.taxon.tipoTaxon}"
name="tipoTaxon">
<f:converter converterId="tipoTaxonConverter" />
<f:selectItem itemLabel="Seleccione uno" itemValue="0" />
<f:selectItems value="#{tipoTaxonDM.tiposTaxones}" var="txn"
itemValue="#{txn.idTipoTaxon}" itemLabel="#{txn.nombreTipo}" />
<p:ajax render="test" />
</p:selectOneMenu>
<p:inputText id="test" rendered="#{taxonDM.taxon.tipoTaxon != null}" />
As you can see I want to render test when an option is selected. tipoTaxon is basically a table on my database and is class, so I had to make a converter. It seems to work so for now, I am not getting the errors I had before. Now I dont get any errors but "test" is not getting rendered.
I tried the following
#{taxonDM.taxon.tipoTaxon != null}
also
#{taxonDM.taxon.tipoTaxon.idTipoTaxon != null}"
I tried setting test on another panel
<h:panelGrid columns="2" id="formTaxon">
<h:outputLabel value="Nombre Científico Taxón" for="taxonInput" />
<p:inputText value="#{taxonDM.taxon.nombreCientificoTaxon}"
id="taxonInput" />
<h:outputLabel value="Nombre Común" for="nombreComunInput" />
<p:inputText value="#{taxonDM.taxon.nombreComunTaxon}"
id="nombreComunInput" />
<h:outputLabel value="Tipo" for="tipoTaxon" />
<p:selectOneMenu id="tipoTaxon" value="#{taxonDM.taxon.tipoTaxon}"
name="tipoTaxon">
<f:converter converterId="tipoTaxonConverter" />
<f:selectItem itemLabel="Seleccione uno" itemValue="0" />
<f:selectItems value="#{tipoTaxonDM.tiposTaxones}" var="txn"
itemValue="#{txn.idTipoTaxon}" itemLabel="#{txn.nombreTipo}" />
<p:ajax render="formTaxon2" />
</p:selectOneMenu>
</h:panelGrid>
<h:panelGrid columns="2" id="formTaxon2">
<p:inputText id="test" rendered="#{taxonDM.taxon.tipoTaxon != null}" />
</h:panelGrid>
using render="test" or render="formTaxon2"
I added a listener method to p:ajax and it worked so i know it is getting invoked.
public void tipoTaxonesXX(AjaxBehaviorEvent e){
System.out.println("Working");
}
It did printed "Working" on my console.
My form is not saving either so I guess it has trouble transforming from tipotaxon or number, but it gets null, i ll fix that later.
Here is the converter if someone needs to
import ec.edu.puce.biologia.model.TipoTaxon;
#FacesConverter("tipoTaxonConverter")
public class TipoTaxonConverter implements Converter {
private TipoTaxonDao tipoTaxonDao;
#Override
public Object getAsObject(final FacesContext arg0, final UIComponent arg1,
final String value) {
if (value == null || !value.matches("\\d+")) {
return null;
}
try {
TipoTaxon tipoTaxon = tipoTaxonDao.recuperar(Long.valueOf(value));
System.out.println("Getting the operation value = "
+ tipoTaxon.getNombreTipo());
return tipoTaxon;
} catch (NumberFormatException e) {
return null;
// throw new ConverterException(new
// FacesMessage("Unknown operation ID: " + value));
} /*
* catch (EntidadNoEncontradaException e) { throw new
* ConverterException(new FacesMessage("Unknown operation ID: " +
* value)); }
*/
}
#Override
public String getAsString(final FacesContext arg0, final UIComponent arg1,
final Object value) {
if (!(value instanceof TipoTaxon)
|| ((TipoTaxon) value).getIdTipoTaxon() == null) {
return null;
}
return String.valueOf(((TipoTaxon) value).getIdTipoTaxon());
}
}
I need to put some exceptions
UPDATE ANSWER
My code here has many errors, I changed it a lot, but the main problem was that the EJB on the converter didnt work. I ended up using ManagedBean. More on that here http://balusc.blogspot.com/2011/09/communication-in-jsf-20.html#ProcessingGETRequestParameters
As I said in my previous answer to your question, both <p:selectOneMany> value must point to your user class, TipoTaxon, and every <f:selectItem>/<f:selectItems> itemValue must also point that same user class, TipoTaxon.
As you see, neither itemValue="0", nor itemValue="txn.nombreTipo" satisfies the abovementioned statement. Correct it and see it working.
My advice for future postings will be to post the complete, relevant and necessarily formatted code, which in your case includes converter code, your model class and managed bean parts. Also, do not post the same question twice/thrice, etc. and instead try to work it out on your own, otherwise it'll be closed as duplicate.

Passing value from data table to bean JSF with Confirm Dialog

I'm having trouble with passing a single value from the data table to the backing bean. I always get the value of 0 when I try to print it in the method in the confirm dialog but when I try to print it in the method in the command button, it shows the value that I need. I think it resets the value or whatever.
<p:dataTable id="labLists" var="lab" value="#{coltsysHome.laboratory}" >
.....
<p:column headerText=" ">
<p:commandButton value="DELETE" onclick="confDlg.show()" icon="ui-icon-closethick" action="#{coltsysHome.action}">
<f:setPropertyActionListener value="#{lab.lab_id}" target="#{coltsysHome.lab_id_del}" />
</p:commandButton>
</p:column>
For the confirm dialog:
<p:confirmDialog widgetVar="confDlg" header="DELETE LABORATORY" message="Are you sure you want to delete this lab?">
<h:form id="delDlgForm">
<p:commandButton id="confirm" value="Yes Sure" oncomplete="confDlg.hide()" actionListener="#{coltsysHome.deleteLab(event)}"/>
<p:commandButton id="decline" value="Not Yet" onclick="confDlg.hide()" type="button" />
</h:form>
Bean (RequestScoped):
...getter and setter (lab_id_del)
public void deleteLab(ActionEvent event) {
FacesContext context = FacesContext.getCurrentInstance();
String cpath = context.getExternalContext().getRequestContextPath();
try (Connection conn = dataSource.getConnection()) {
ColtsysDAO coltsysDAO = new ColtsysDAO(conn);
coltsysDAO.deleteLab(lab_id_del, coltsysDAO.getUserID(getUser_name()));
} catch (Exception e) {
e.printStackTrace(System.err);
}
}
public void action() {
System.out.println("lab_id_del: " + lab_id_del);
}
The first and foremost change you need to do is: RequestScoped->ViewScoped as Luiggi Mendoza has suggested. you might need to add process attribute to the p:commandButton with the id of the p:dataTable. Also you can pass the lab object directly into action method without using f:setPropertyActionListener as: action="#{coltsysHome.action(lab)}" and take it as: public String action(Lab lab). Why not actionListener!

primefaces trying to add a p:message for validation errors and p:growl for success messages

I have a page with a dialog where a user can add a project. I want the server side validation error messages to be displayed in a p:message tag, but if the validation passes, i want the success message to be displayed in a p:growl tag and the dialog to close.
How can i achieve this? Currently i can get all messages (error or success) to appear in either all p:growl, all p:messages, or all in both.
<p:growl id="growl" showDetail="true" life="3000" autoUpdate="false" />
<p:dialog id="projectsDialog" header="Add Project" widgetVar="projectsDialog" modal="true">
<p:messages id="messages" autoUpdate="false" showDetail="true" />
<h:form>
<h:outputLabel for="investigator" value="Investigator" />
<p:selectOneMenu value="#{ppBacker.investigatorKey}" id="investigator" size="1" styleClass="listBoxMedium" >
<f:selectItems value="#{ppBacker.investigators}" />
</p:selectOneMenu>
<h:outputLabel for="startDate" value="Start Date" />
<p:calendar value="#{ppBacker.startDate}" id="startDate" mode="popup" navigator="true" required="true" requiredMessage="Start Date is required" />
<h:outputLabel for="endDate" value="End Date" />
<p:calendar value="#{ppBacker.endDate}" id="endDate" mode="popup" navigator="true" required="true" requiredMessage="End Date is required" />
<p:commandButton value="Add Project" id="s1Btn" type="submit" oncomplete="handleLoginRequest(xhr, status, args)" actionListener="#{ppBacker.addProject}" update=":growl">
<f:setPropertyActionListener target="#{ppBacker.grantKey}" value="#{ppBacker.grantKey}" />
</p:commandButton>
<!-- this jquery closes the add project dialog if the server side validation is successful -->
<script type="text/javascript">
function handleLoginRequest(xhr, status, args) {
console.log('args.success = '+args.success);
console.log('args.validationFaile = '+args.validationFailed);
if(args.validationFailed || !args.success) {
jQuery('#projectsDialog').effect("shake", { times:1 }, 100);
} else {
projectsDialog.hide();
}
}
</script>
</h:form>
My ppBacker.addProject method
public void addProject(){
RequestContext context = RequestContext.getCurrentInstance();
FacesMessage msg = null;
boolean success = false;
// validate the investigator, start date, and end date are set
if(!getProject().getInvestigatorKey().equals("0") && getProject().getStartDate() != null && getProject().getStartDate() != null) {
success = true;
msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Project Added Successfully", "");
} else {
success = false;
msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Validation Failed", "Investigator, Start Date, and End Date are required");
}
System.out.println("success = "+success);
// I'M GUESSING THAT I CAN SOMEHOW USE THE FIRST PARAMETER OF THIS TO ATTACH MY MESSAGE, BUT I'M NOT SURE
FacesContext.getCurrentInstance().addMessage(null, msg);
context.addCallbackParam("success", success);
}
Make use of "globalOnly" feature. On FacesContext#addMessage() call, give all error messages a non-null client ID (e.g. the one of the input component, or the button component or even the form component) and give all success messages a null client ID.
This way you can get away with:
<p:messages ... globalOnly="false" />
<p:growl ... globalOnly="true" />
An alternative is to rely on #{facesContext.validationFailed} in the rendered attribute. When manually adding an error message, call FacesContext#validationFailed().
if (error) {
context.addMessage(null, errorMessage);
context.validationFailed();
} else {
context.addMessage(null, successMessage);
}

Categories