zondag 10 november 2013

ADF 11g: create your own audit-rules and automated fixes

Introduction

In my previous post ADF BC: performance-issues when using View Accessors and List of Values I described the problems that can occur when defining a View Accessor and a related List of Values. JDeveloper sets / changes the View Accessors property 'Row Level Bind Values', and the value set can have a negative effect on the number of queries fired, and thus on the performance of our application.

In this post I will describe a JDeveloper extension that implements an audit-rule and a fix for the Row Level Bind Values property. First I will describe how the extension is build. and after that I will describe how to use the extension in JDeveloper. The full source of the extension can be downloaded from AuditExtensions.zip


Building the extension

The extension is based on the blog Don't fear the Audit -- Part 1 and the samples found in Oracle JDeveloper 11g: Oracle IDE Extension Samples. These samples can be found in your JDeveloper installation directory; search for esdksamples.zip.
Based on the above resources, I've worked out an extension that consists of the following structure:.




AuditRulesAddin
This is the class that defines the Analyzer-classes that are part of this extension. We can include multiple Analyzers if we want to.


 public class AuditRulesAddin extends AbstractAuditAddin {  
   private static final Class[] ANALYZERS = new Class[] { RowLevelBindingAnalyzer.class };  
   
   public Class[] getAnalyzers() {  
     return ANALYZERS;  
   }  
 }  
   


Res.properties 
This is a file that contains labels and descriptions used for this extension.

RowLevelBindingAnalyzer
This is the class that performs the actual analysis of the objects of your project and it reports the errors it finds when we run the audit. One analyzer class can contain multiple audit-rules. Our example only contains one rule. The header of the class defines the rules:



 public class RowLevelBindingAnalyzer extends Analyzer {  
   
   // Refers to properties file with descriptions and labels  
   private static final Localizer LOCALIZER = Localizer.instance("anubis.auditrules.Res");  
   // Defines the category for the audit rules  
   private final Category CATEGORY = new Category("category-vo-checks", LOCALIZER);  
   
   // Definition of the fix.  
   private static final String FIX_RLB = "set-rlb-value";  
   
   // Definition of the tranformation-class that performs the fix.  
   private final RowLevelBindingTransform fixRlb = new RowLevelBindingTransform(FIX_RLB, LOCALIZER);  
   
   // Definition of the actual rule, including the related tranformation-class.   
   // It is possible to define more than one rule in an analyzer-class  
   private Rule INVALID_RLB = new Rule("rule-invalid-rlb", CATEGORY, 
                                                        Severity.ERROR, LOCALIZER, fixRlb);  
      
   {  
     // Do this to make the rule enabled by default.  
     INVALID_RLB.setEnabled(true);  
   }  
   
   /**  
    * @return Define the rules that are validated by this analyzer  
    */  
   public Rule[] getRules() {  
     return new Rule[] { INVALID_RLB };  
   }  
  ....  

The class extends the class oracle.jdeveloper.audit.analyzer.Analyzer and has 4 important methods:


  • public void enter(AuditContext context, Workspace workspace)
    This methods contains the workspace (application) validation code. In our case we do not use it.
  • public void enter(AuditContext context, Project project)
    This methods contains the project validation code. In our case we do not use it.
  • public void enter(AuditContext context, Document document)

    This method contains the code to analyze and validate documents. In our case this method is used to enable the validation of XML-files containing the ViewObject definition.


   /**  
    * Enter a document.  
    * Check to see if this is a ViewObject file. If not, we can stop here.  
    * @param context  
    * @param document  
    */  
   public void enter(AuditContext context, Document document) {  
     if (document != null && document.getDocumentElement() != null) {  
       String firstNodeOfdocument = document.getDocumentElement().getNodeName();  
       if ("ViewObject".equals(firstNodeOfdocument)) {  
         String filePath = context.getUrl().getPath();  
         if (!filePath.substring(filePath.lastIndexOf("/") + 1).matches(".*xml")) {  
           setEnabled(false);  
         }  
       }  
     }  
   }  


  • public void enter(AuditContext context, Element element)

    This method contains the code to analyze and validate individual element. In our case this is the method that is doing the actual work. It determines the actual setting and counts the number of row level bound bind variables. If something is wrong, a ViolationReport is created.
   /**  
    * Enter an element.  
    * This is where we will check the value of the RowLevelBinds element and report  
    * the results back to the Editor.  
    * @param context  
    * @param element  
    */  
   public void enter(AuditContext context, Element element) {  
     if ("ViewAccessor".equals(element.getNodeName())) {  
       String currentrlb = element.getAttribute("RowLevelBinds");  
       int cntrlb = 0;  

       // Count the number of row level bind params. These params do not contain '"' (double-quote).  
       // First, get the paramMap, second, find the transient expression that holds the bind-vaiable  
       for (Node childNode = element.getFirstChild(); childNode != null; ) {  
         Node nextNode = childNode.getNextSibling();  
         if ("ParameterMap".equals(childNode.getNodeName())) {  
           for (Node param = childNode.getFirstChild(); param != null; ) {  
             Node nextChild = param.getNextSibling();  
             Node expression = param.getFirstChild();  
             if (expression != null && "TransientExpression".equals(expression.getNodeName())) {  
               String value = expression.getTextContent();  
               if (value != null && value.indexOf('"') == -1) {  
                 cntrlb++;  
               }  
             }  
             param = nextChild;  
           }  
         }  
         childNode = nextNode;  
       }
  
       String newrlb = cntrlb == 0 ? "false" : "true";  
       if ( // Empty rlb expression  
         (currentrlb == null || currentrlb.trim().length() == 0) ||  
         // rlb = true but there are no row level bound variables  
         ("true".equals(currentrlb) && cntrlb == 0) ||  
         // rlb = fals but there are row level bound variables  
         ("false".equals(currentrlb) && cntrlb != 0)) {  

             // Create a violation report for our specific rule.
             ViolationReport report = context.report(INVALID_RLB);  
             report.addParameter("currentrlb", currentrlb);  
             report.addParameter("newrlb", newrlb);  
             report.addParameter("cntrlb", cntrlb);  
       }  
     }  
   }  


RowLevelTransform
This class is called from the audit-framework to transform the XML-elements that have violated the audit-rule, and thus, for which a ViolationReport is created. It calls the RowLevelBindingFix class to do the actual fixing. See the full source for more details.

RowLevelBindingFix
This class performs the actual fixing of the XML-elements that violate the audit-rule. In our case this class determines and sets the correct value of the ViewAccessors attribute 'RowLevelBinds'. See the full source for more details.

extension.xml
This file contains the definition of the extension. It defines the classes used, and it defines our AuditRulesAddin as an addin in JDeveloper.


 <extension id="anubis.auditrules" version="1.0" esdk-version="1.0"  
       rsbundle-class="anubis.auditrules.Res"  
       xmlns="http://jcp.org/jsr/198/extension-manifest">  
  <name>Demo Audit Rules</name>  
  <dependencies>  
   <import>oracle.ide</import>  
   <import>oracle.ide.audit</import>  
   <import>oracle.ide.audit.core</import>  
   <import>oracle.ide.xmlef</import>  
   <import>oracle.jdeveloper.xmlef</import>  
   <import>oracle.jdeveloper</import>  
   <import>oracle.javacore</import>  
   <import>oracle.jdeveloper.refactoring</import>  
  </dependencies>  
  <classpaths>  
   <classpath>../../../oracle_common/modules/oracle.javatools_11.1.1/javatools-nodeps.jar</classpath>  
  </classpaths>  
  <hooks>  
   <jdeveloper-hook xmlns="http://xmlns.oracle.com/jdeveloper/1013/extension">  
    <addins>  
     <addin headless="true">anubis.auditrules.AuditRulesAddin</addin>  
    </addins>  
   </jdeveloper-hook>  
  </hooks>  
 </extension>  


Deploying and using the extension 

To use the extension in JDeveloper perform the following steps:
  • Deploy the project to a jar-file (in our case anubis.auditrules.jar). 
  • Copy the jar-file to the extension-directory of our JDeveloper installation. This directory can be found under <MIDDLEWARE-HOME>\jdeveloper\jdev\extensions. 
  • Restart JDeveloper
Now we are ready to use the extension. Suppose we take the project as described in my previous post. This project contains a Viewobject EmployeesView that contains a ViewAccessor that refers to the DepartmentsView. The property 'Row Level Bind Values' is set to 'true', which is not correct.




To run our new audit-rule perform the following steps:


  • Select the model-project in the navigator.
  • Select the menu-option 'Build -> Audit Model.jpr'
  • Select the 'Audit rules' profile and edi this profile.
  • Find the 'Anubis VO checks' category and enable it. 
  • Optionallly: disable the other categories and save the profile under a new name.




  • Run the audit. The result will show all VO's that contain ViewAccessors with an incorrect value for the Row Level Binding Values property. 




  • Fix the problem by using the 'Apply Default Fixes' icon from the toolbar. All errors that are solved will be marked.

The above example only shows one error and one fix, but when you run this rule on a large project, you can find and fix all audit-errors at once.

Conclusion

As we have seen it is possible to create our own audit-rules and related fixes. This can be of great help in improving the quality of your project. There is an increasing number of 'standard' audit-rules available in JDeveloper. However it can still be very usefull to create your own rules, especially in combination with the option of defining your own fixes. 

1 opmerking:

  1. Hi,

    It is very useful for me. Could you please let me know if we can run custom jaudit from command line? Can you provide me the steps to do this if we can.

    BeantwoordenVerwijderen