Nov 24, 2011

To override the default error handler one can extend the  DCErrorHandlerImpl. In this you can process the exceptions or can choose to suppress them. In this post i will explain a way to suppress the RowValException message and display only the detail entity attribute level exception ie AttrValException.

In ADF the exceptions are grouped and raised together as a single object. For example let's say that 2 of the attributes fail the mandatory validation then the exceptions raised for them will be 2 individual AttrValException these are then attached as detail objects into a RowValException and if there are many such RowValException they will be attached and raised together as a TxnValException.

So now to handle the aforementioned task we need to recursively  process the exceptions and suppress the RowValException message. The snippet below shows that :-

public class MyErrorHandler extends DCErrorHandlerImpl {
    private static final ADFLogger logger = ADFLogger.createADFLogger(VleAdminErrorHandler.class);
    private static ResourceBundle rb = ResourceBundle.getBundle("mypackage.myBundle");
    public MyErrorHandler() {
        super(true);
    }

    public MyErrorHandler(boolean setToThrow) {
        super(setToThrow);
    }

    public void reportException(DCBindingContainer bc, java.lang.Exception ex) {
        disableAppendCodes(ex);
        logger.info("entering reportException() method");
        BindingContext ctx = bc.getBindingContext();
        String err_code;
        err_code = null;
        if(ex instanceof NullPointerException){
              logger.severe(ex); 
              JboException e = new JboException(rb.getString("STANDARD_ERROR_MESSAGE"));
              super.reportException(bc, e);
            }
        else if(ex instanceof RowValException){
          
          Object[] exceptions= ((RowValException)ex).getDetails();
          if(exceptions!=null){
            for(int i=0;i<exceptions.length;i++){
                  if(exceptions[i] instanceof RowValException){
                       this.reportException(bc, (Exception)exceptions[i]);                                 
                  }
                  else if(exceptions[i] instanceof AttrValException){
                super.reportException(bc, (Exception)exceptions[i]);
                  }
              }
          }
            else{
            this.reportException(bc, ex);
            }
            
        }
       else if (ex instanceof TxnValException) {
          Object[] exceptions= ((TxnValException)ex).getDetails();
          if(exceptions!=null){
          for(int i=0;i<exceptions.length;i++){
              if(exceptions[i] instanceof RowValException){
                   this.reportException(bc, (Exception)exceptions[i]);                                 
              }
              else{
            super.reportException(bc, (Exception)exceptions[i]);
              }
          }
          }
          else {
                super.reportException(bc, ex);
              }
        }
      
       else if (ex instanceof oracle.jbo.DMLException) {
           JboException e = new JboException(rb.getString("STANDARD_ERROR_MESSAGE"));
            super.reportException(bc, e);
        }
       else if(ex instanceof javax.xml.ws.WebServiceException){
            JboException e=new JboException(rb.getString("WEB_SERVICE_EXCEPTION"));
            super.reportException(bc, e);
            }
       
        else if (ex instanceof JboException) {
            super.reportException(bc, ex);
        }

    }


    public static FacesMessage getMessageFromBundle(String key, FacesMessage.Severity severity) {
        ResourceBundle bundle = ResourceBundle.getBundle("sahaj.apps.vleadministration.view.resources.VLEAdministrationUIBundle");
        String summary = JSFUtil.getStringSafely(bundle, key, null);
        String detail = JSFUtil.getStringSafely(bundle, key + "_detail", summary);
        FacesMessage message = new FacesMessage(summary, detail);
        message.setSeverity(severity);
        return message;
    }
  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  
    protected boolean skipException(Exception ex) {  
      if (ex instanceof JboException) {  
        return false;  
      } else if (ex instanceof SQLIntegrityConstraintViolationException) {  
        return true;  
      }  
      return super.skipException(ex);  
    }

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

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

In this snippet i am recursively processing TxnValException->RowValException -> AttrValException. The AttrValException is being passed in the super call to the DCErrorHandlerImpl to process and display the message.




Posted on Thursday, November 24, 2011 by Ramandeep Singh Nanda

Nov 17, 2011

I had a recently asked a question on bulk uploads in OTN forum. Seems the performance that i would receive for bulk update/insert scenarios isn't at par.
So i created the procedure and a custom object type and table of that object type which provides far better performance.

Let's say your table structure is as follows :-

--     TXN_TBL
("TXN_ID" Number,
"USER_NAME" VARCHAR2(50 BYTE),
"TXN_DATE" DATE,
"TXN_AMOUNT" NUMBER)

So you can basically create a object type mirroring that structure as follows
create or replace type TXN_TBL_R is object
("TXN_ID" Number,
"USER_NAME" VARCHAR2(50 BYTE),
"TXN_DATE" DATE,
"TXN_AMOUNT" NUMBER)
and then create a table type that will store record of these types:
create or replace type TXN_TBL_TB as table of  TXN_TBL_R
The procedure that will perform the updates and or inserts is shown in the below snippet:-

create or replace procedure B_INSERT(p_in IN TXN_TBL_TB) as
cursor for_insert is select * from TABLE(p_in) rt where not exists(select tmp.TXN_ID from TXN_TBL tmp where rt.TXN_ID=tmp.TXN_ID);
cursor for_update is select * from TABLE(p_in) rt where exists(select tmp.TXN_ID from TXN_TBL tmp where rt.TXN_ID=tmp.TXN_ID);
temp_insert TXN_TBL_R;
temp_update TXN_TBL_R;
begin
for temp_update in for_update loop
update TXN_TBL tmp set tmp.USER_NAME= temp_update.USER_NAME,tmp.TXN_DATE=temp_update.TXN_DATE,tmp.TXN_AMOUNT=temp_update.TXN_AMOUNT where tmp.TXN_ID= temp_update.TXN_ID;
end loop;
for temp_insert in for_insert loop
insert into TXN_TBL values(temp_insert.TXN_ID, temp_insert.USER_NAME, temp_insert.TXN_DATE, temp_insert.TXN_AMOUNT);
end loop;
end;

Then you can basically call this program from the ADF application using struct type. The snippet is shown below:-
/**
*@param valueSet the set of bean values
*/
 public void someMethod(Set valueSet){
         Connection conn=null;
        try {
          conn = getDBTransaction().createStatement(1).getConnection();
          StructDescriptor tblRecordStructType =
                          StructDescriptor.createDescriptor("TXN_TBL_R", conn);
        Iterator it= valueSet.iterator();
        Object txnArray[]=new Object[set.size()];
          while(it.hasNext()){
       SomeCustomBean detail=it.next();
  STRUCT tempStruct=new STRUCT(tblRecordStructType,conn,new Object[]{detail.getTranxId(),detail.getUserName(),detail.getTxnDate(),detail.getTransAmount()});  
    txnArray[i]=tempStruct;
    i=i+1;
    }
    //create array structure descriptor
     ArrayDescriptor txnTableDesc=ArrayDescriptor.createDescriptor("TXN_TBL",conn);
  //create an Array type with given structure definition
  ARRAY txnTableArray=new ARRAY(txnTableDesc,conn,txnArray); 
           String callableProcedureStatement=" begin  TMP_INSERT(?); end;" ;
          OracleCallableStatement st=null;
          st=(OracleCallableStatement)getDBTransaction().createCallableStatement(callableProcedureStatement, 0);  
          st.setARRAY(1, txnTableArray);
          st.executeUpdate();  
    this.getDBtransaction().commit();
        } catch (JboException e) {
this.getDBtransaction().rollBack();
         throw new JboException(e.getMessage());
   
        }
  }


This is basically it. This will perform faster than normal ADF update/insert.


Posted on Thursday, November 17, 2011 by Ramandeep Singh Nanda

If you are using ADF11g version 11.1.1.3 and want to reset the values in the popup, you are not provided with the resetEditableValues option. But you can accomplish this using a custom javascript that i have made.

All you have to do is set clientComponent="true" for all the components that you want to reset and pass the client id of the root component to use this.

resetAction=function(clientId){
    var component= AdfPage.PAGE.findComponentByAbsoluteId(clientId);
    console.log(component);
    var components=component.getDescendantComponents();
    this.resetFunction(components);
    }
   resetFunction=function(components){
     for (var i=0;i<components.length;i++){
     if(components[i] instanceof AdfRichInputText){
     if(components[i].getVisible()&&!components[i].getDisabled()&&!components[i].getReadOnly()){
     components[i].setValue("");
     }
     }
     if(components[i] instanceof AdfRichSelectOneChoice){
    if(components[i].getVisible()&&!components[i].getDisabled()&&!components[i].getReadOnly()){
    components[i].resetValue();
    }
     }
     }
  }
   
    
Save the contents of this file to a JS file and then import it in your fragment or a jsf page.

Now you have to bind the parent container like panelFormLayout and then on action of the command button use ExtendedRenderKitService to pass the client id of the root component dynamically to resetAction Method so that this script always works. The snippet is shown below.

/**
     * Resets the popup
     * @return null
     */
    public String resetPopupAction() {
      FacesContext context = FacesContext.getCurrentInstance();
      ExtendedRenderKitService erks =
      Service.getRenderKitService(context, ExtendedRenderKitService.class);
      erks.addScript(context,"resetAction('"+formBinding.getClientId(context) +"')"); 
    
      return null;
    }


Hope this will be helpful :D

Posted on Thursday, November 17, 2011 by Ramandeep Singh Nanda