Tuesday 9 August 2016

Exception Handling in adf (Part 1)


Exception Handling in ADF

Since Exception handling is very vital part of any web based application and technique of exception handling implementation  is depend on from where it is throwing.
The exception will occurred on following part of the application

1-Model
2-View Controller
It may be Model layer or may be View layer

Moreover the Exception handling approach is base on the layer from where it's thrown .If it is throwing through Model layer then the way of handling is difference compare to if it thrown on the View Controller .

As you all know we are accessing model layer through the binding container. Internally If any exception has thrown by model layer ,then by default the DCErrorHandlerImpl class going to catch the exception and show the  error message to user.(this is default features of ADF).

In some case we do not want to show the exception because as end user they want more specify message.So in this case we need to override default exception .

So first i would like to explain exception handling approach in model layer


Following step will explain the way of exception in model.

1-Create the class and extends with DCErrorHandlerImpl
2-Override the public void reportException(DCBindingContainer bc, Exception ex) .

According your requirement report the exception on DCErrorHandlerImpl.
package com.prateek.blog.view.exception;

import oracle.adf.model.binding.DCBindingContainer;
import oracle.adf.model.binding.DCErrorHandlerImpl;

import oracle.jbo.JboException;

public class CustomExceptionHandler extends DCErrorHandlerImpl {
    public CustomExceptionHandler() {
        super(true);
    }

    @Override
    public void reportException(DCBindingContainer dCBindingContainer,
                                Exception exception) {

        if (exception instanceof JboException) {
            //By default all exception which are coming from Model layer all are instance of JboException
            //here just i am try to skip the exception to report to super class which is DCErrorHandlerInpl
            //we can write our own logic
            // if the exception came of model then it always go through this if condition
        } else {
            super.reportException(dCBindingContainer, exception);
        }

    }

    private void disableAppendCodes(Exception ex) {
        if (ex instanceof JboException) {
            JboException jboEx = (JboException)ex;
            jboEx.setAppendCodes(false);
            Object[] detailExceptions = jboEx.getDetails();
            if ((detailExceptions != null) && (detailExceptions.length > 0)) {
                for (int z = 0, numEx = detailExceptions.length; z < numEx;
                     z++) {
                    disableAppendCodes((Exception)detailExceptions[z]);
                }
            }
        }
    }

    @Override
    public DCErrorMessage getDetailedDisplayMessage(BindingContext bindingContext,
                                                    RegionBinding regionBinding,
                                                    Exception exception) {
        return super.getDetailedDisplayMessage(bindingContext, regionBinding,
                                               exception);
    }

    @Override
    public String getDisplayMessage(BindingContext bindingContext,
                                    Exception exception) {
        return super.getDisplayMessage(bindingContext, exception);
    }

    @Override
    protected boolean skipException(Exception exception) {
        return super.skipException(exception);
    }

}

*This is just a example to just skip the exception.Please see the following link for more information.

1-http://docs.oracle.com/cd/B31017_01/web.1013/b25947/web_val008.htm

3-After Creating our own custom exception handler we need to write this class in DataBinding.cpx as ErrorHandlerClass

<application clienttype="Generic" errorhandlerclass="com.prateek.blog.view.exception.CustomExceptionHandler" id="DataBindings" package="com.prateek.blog.view" separatexmlfiles="false" version="11.1.1.59.23" xmlns="http://xmlns.oracle.com/adfm/application">
  <pagemap>
    <page path="/exceptionPage.jspx" usageid="com_prateek_blog_view_exceptionPagePageDef">
  </page></pagemap>
  <pagedefinitionusages>
    <page id="com_prateek_blog_view_exceptionPagePageDef" path="com.prateek.blog.view.pageDefs.exceptionPagePageDef">
  </page></pagedefinitionusages>
  <datacontrolusages>
    <bc4jdatacontrol configuration="AppModuleLocal" factoryclass="oracle.adf.model.bc4j.DataControlFactoryImpl" id="AppModuleDataControl" package="com.prateek.blog.model" supportsfindmode="true" supportsrangesize="true" supportsresetstate="true" supportssortcollection="true" supportstransactions="true" syncmode="Immediate" xmlns="http://xmlns.oracle.com/adfm/datacontrol">
  </bc4jdatacontrol></datacontrolusages>
</application>

4-The following code which i have written in to backing bean  to show our custom message on UI.

    public void jboException(ActionEvent actionEvent) {
    
    BindingContainer bindings =
            BindingContext.getCurrent().getCurrentBindingsEntry();
        OperationBinding opeartionBinding =
            bindings.getOperationBinding("throwNewException");
        opeartionBinding.execute();
        if (opeartionBinding.getErrors().size() > 0) {
            FacesContext context = FacesContext.getCurrentInstance();
            FacesMessage message = new FacesMessage("Error message here");
            message.setSeverity(FacesMessage.SEVERITY_ERROR);
            context.addMessage(null, message);
        }

    }

    public void numberPointerException(ActionEvent actionEvent) {
        BindingContainer bindings =
            BindingContext.getCurrent().getCurrentBindingsEntry();
        OperationBinding opeartionBinding =
            bindings.getOperationBinding("throwNewException1");
        opeartionBinding.execute();
        if (opeartionBinding.getErrors().size() > 0) {
            FacesContext context = FacesContext.getCurrentInstance();
            FacesMessage message = new FacesMessage("Error message here");
            message.setSeverity(FacesMessage.SEVERITY_ERROR);
            context.addMessage(null, message);
        }
    }

This exception handling approach is not going to catch any exception which is thrown from UI 

We can handle Exception Handling in Model layer as following ways

1-For Single Application and handle as global for model layer(Binding layer) :
(If you have only on DataBinding.cpx file ) 
Create the Class which extend DCErrorHandlerImpl  and register with DataBinding.cpx file.It means all the page def file which is associated with this DataBinding.cpx  file if throws any exception then Extends Class is going to catch the exception .
So this approach is work whole in model layer regardless what ever  page def is associated with the DataBinding.cpx .

2-Only for individual page def file (Binding layer):
Second Approach is going to handle the exception only for  one page where we register our custom class as a ControllerClass .

3-For N number of application and handle as global for all  model layer(Binding layer) :
(If you have only DataBinding.cpx file or more than one ) . (Top most application has Error Handler)
Suppose if you  have a big application which contains N number of projects.Therefore each individual project have their own DataBinding.cpx file.If you implement the first approach you need to create N number file for each individual Class file which extends DCErrorHandlerImlp class.So over come this type of issue Oracle Adf provide one more type of Model layer Exception handling.Where you need to extends one ADF life cycle class,One JSF life cycle class and need to register the custom JSF life cycle class into faces-config.xml.
So in this case if exception thrown in any application but from model layer(binding layer) then  custom class will going to catch the exception .

After reading above points ,I am sure it will give the clear ideas of Exception Handling Approach in Model Layer.

If your top most application has error handler register then you do not required to inject Error handler class using above approach.

Let me explain above point into details

1-For Single Application and handle as global for model layer : Since my last exception handling post Ihave already explain this approach .

2-Only for individual page def file : You can handle exception in model layer for only one single page .For implementing this approach do following step

i-Create the class and extends with FacesPageLifecycle class
ii- override following method  
public void reportErrors(PageLifecycleContext pageLifecycleContext){};
iii-Then register this newly created class into page def file in ControllerClass  file .
iv-This method always being called when any exception thrown from same page. 

3-For N number of application and handle as global for all model layer (Binding Layer):  This approach will help full if you dealing with a large application which contains n number of project.And you do not want each individual application owner to implement the exception handling.

Let me explain my use case where we have implemented this approach ,

We have a  very large application so  we have divided our large application into  simple module .and after that we have created on main application where we add this application as jar or war.It mean through this main application we will access our module.

Hence we have implemented this approach.

So for this we have created one new application where we have written all exception handling related code.And after that we have created the adf jar file which we consumed  into our main application.

Step are following .
1-Create the class and extends with DCErrorHandlerImpl
2-Override the public void reportException(DCBindingContainer bc, Exception ex) .

package com.prateek.blog.view.exception;

import oracle.adf.model.binding.DCBindingContainer;
import oracle.adf.model.binding.DCErrorHandlerImpl;

import oracle.jbo.JboException;

public class CustomExceptionHandler extends DCErrorHandlerImpl {
    public CustomExceptionHandler() {
        super(true);
    }

    @Override
    public void reportException(DCBindingContainer dCBindingContainer,
                                Exception exception) {

        if (exception instanceof JboException) {
            //By default all exception which are coming from Model layer all are instance of JboException
            //here just i am try to skip the exception to report to super class which is DCErrorHandlerInpl
            //we can write our own logic
            // if the exception came of model then it always go through this if condition
        } else {
            super.reportException(dCBindingContainer, exception);
        }

    }

    private void disableAppendCodes(Exception ex) {
        if (ex instanceof JboException) {
            JboException jboEx = (JboException)ex;
            jboEx.setAppendCodes(false);
            Object[] detailExceptions = jboEx.getDetails();
            if ((detailExceptions != null) && (detailExceptions.length > 0)) {
                for (int z = 0, numEx = detailExceptions.length; z < numEx;
                     z++) {
                    disableAppendCodes((Exception)detailExceptions[z]);
                }
            }
        }
    }

    @Override
    public DCErrorMessage getDetailedDisplayMessage(BindingContext bindingContext,
                                                    RegionBinding regionBinding,
                                                    Exception exception) {
        return super.getDetailedDisplayMessage(bindingContext, regionBinding,
                                               exception);
    }

    @Override
    public String getDisplayMessage(BindingContext bindingContext,
                                    Exception exception) {
        return super.getDisplayMessage(bindingContext, exception);
    }

    @Override
    protected boolean skipException(Exception exception) {
        return super.skipException(exception);
    }

}

3- Then need to create custom page life cycle class that extends  FacesPageLifecycle
4-Over ride the Prepare model method  where we need to register newly create customer exception handler class into the setErrorHandler method
public void prepareModel(LifecycleContext ctx)
{
if (!(ctx.getBindingContext().getErrorHandler() instanceof ModelExceptionHandlerClass)) { ctx.getBindingContext().setErrorHandler(new ModelExceptionHandlerClass(true));
}
super.prepareModel(ctx);
 }
here ModelExceptionHandlerClass class my custom exception handler class.

5-Then need to create custom listener class where need to register newly created life cycle class .
6-For this need to extends ADFPhaseListener class .
7-Override  protected PageLifecycle createPageLifecycle(){}

@Override protected PageLifecycle createPageLifecycle() { return new FacesPageLifecycleBean(); }

8-Need to register above class into faces-config.xml file under life cycle property.

Code of the above implementation is following
https://docs.google.com/open?id=0B8cP4jZuxLlXTnRIMkxaWURhazg

This post explain  different type of approach for handling exception in model layer(binding layer).

No comments:

Post a Comment