Customizing Oracle ADF Error Messages in Jdeveloper
Environment
Jdeveloper 10.1.3.2
ADF BC JSF
It is often required to control ADF/JSF error messages in order to display them in an more error friendly format. For example, if you rely on Data Base triggers to implement business rules, or you want constraint violation to be more readable you might as well need to show the business rules violation is different formats.
Background
We need first to understand how the error message are handled by the ADF by default before we attempt to override the default behavior
1- Any exception thrown by ADF application is caught by the Binding Container, in particular by a class called DCErrorHandlerImp.
2- A method called ReportException then can passes the exception back to the Binding Container
3- During Prepare Render Case, the ADFLifecycle executes reportError(context)
How to Customize the default Error Messages
1- you need to write a new errorhandler class that extends the default DCErrorHandlerImp class
2- you need to override the ReportException method and write the necessary code to handle and display the error messages the way you want
3- You want to find a way to tell the lifecycle to execute the new errorhandler instead the default one.
Example:
Implementing steps (1) and (2)
Create a class called AmmarErrorHandler that extends the DCErrorHandlerImpl Class
Override the reportException Method to customize the way you want to display the errors
package view.backing;
import com.evermind.server.Application;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import oracle.adf.model.binding.DCBindingContainer;
import oracle.adf.model.binding.DCErrorHandlerImpl;
import oracle.jbo.DMLConstraintException;
import oracle.jbo.JboException;
import oracle.jbo.Row;
import realsoft.com.helper.JSFHelper;
public class AmmarErrorHandler extends DCErrorHandlerImpl{
/**
* Constructor for custom error handler.
* @param setToThrow should exceptions throw or not
*/
public AmmarErrorHandler(boolean setToThrow) {
super(setToThrow);
}
/**
* Overridden ADF binding framework method to customize the way
* that Exceptions are reported to the client.
* @param bc BindingContainer
* @param ex exception being reported
*/
public void reportException(DCBindingContainer bc, Exception ex) {
FacesContext.getCurrentInstance().addMessage("myMessage",new FacesMessage(FacesMessage.SEVERITY_ERROR,"Test Message is my message",null));
this.displayProperly( ex, bc);
// super.reportException(bc, ex);
}
private void displayProperly(Exception ex, DCBindingContainer bc )
{
if( ex instanceof oracle.jbo.JboException )
System.out.println( ex.getClass().getName() + " :: at tryToCommit(){ commit() ; } , Details : " + ((oracle.jbo.JboException)ex).getDetailMessage() ) ;
DMLConstraintException dmlEx ;
Row proRow ;
String[] attsNames ;
Object[] attsValues ;
if( ex instanceof DMLConstraintException )
{
dmlEx = ((DMLConstraintException)ex) ;
proRow = dmlEx.getEntityRow() ;
attsNames = proRow.getAttributeNames() ;
attsValues = proRow.getAttributeValues() ;
JSFHelper.addFacesErrorMessage(dmlEx.getErrorCode());
JSFHelper.addFacesErrorMessage(dmlEx.getConstraintName());
for( int i = 0 ; i < proRow.getAttributeCount() ; i ++ )
log.error( attsNames[i] + " = " + attsValues[i] ) ;
}
// if you want to print the entire error stack JSFHelper.addFacesErrorMessage(ex.getMessage());
}
}
Now , we need to implement step (3). In order to make the application use the AmmarErrorHandler class created above, we need to create a custom lifecycle class that override prepareModel. In this class, we test if the we instruct the PageLifeCycle to execute the AmmarErrorHandler Instead of the default errorHanlderImpl class
package view.backing;
import oracle.adf.controller.faces.lifecycle.FacesPageLifecycle;
import oracle.adf.controller.v2.context.LifecycleContext;
public class customLifecycle extends FacesPageLifecycle
{
public void prepareModel(LifecycleContext ctx) {
if (!(ctx.getBindingContext().getErrorHandler() instanceof
AmmarErrorHandler)) {
ctx.getBindingContext().setErrorHandler(new AmmarErrorHandler(true));
}
//etc
super.prepareModel(ctx);
}
}
The custom lifecycle needs to be executed as part of the ADFLifecycle, so we still need to create yet another class that extends ADFPhaseListener and in its construct instantiates our previously created customLifecycle.
package view.backing;
import oracle.adf.controller.faces.lifecycle.ADFPhaseListener;
import oracle.adf.controller.v2.lifecycle.PageLifecycle;
public class MyAdfPhasesLitener extends ADFPhaseListener {
protected PageLifecycle createPageLifecycle() {
return new customLifecycle();
}
}
To clarify, the following dependencies are shown
MyAdfPhasesListener à Returns customLifecycle() à makes AmmarErrorHandler the default error handling routine
Finally, the Faces-config needs to be configured
Running a simple example with Unique key violation