Thursday, October 20, 2016

ADF: Know if VO attribute is currently changed

Problem description: We have isAttributeChanged(String) method available in VORowImpl, which can tell if a particular attribute in vo row has changed or not. Problem with this method is if we change attribute once and then restore its value back, method still returns that attribute has changed. I would not say this is bug as this might be requirement at times to know if attribute is ever changed. but at times we are interested in knowing if attribute's current value is different from the database value.
In this blog I want to come up with another method isAttributeCurrentlyChanged(String), which can compare database value with current value and let us know if attribute is actually changed at this time. If value is restored, we should get false as there is no effective change.

Solution: Idea is to use getPostedAttribute method of entity to get database value and compare it with attribute's current value to know if its actually changed. We also want to add this method to be available on each vo row so we would be writing our own base classes and introduce method in those.

1. Create an Entity Impl (say SanEntityImpl) base class and introduce a method getDataBaseValue as

import oracle.jbo.server.EntityImpl;
public class SanEntityImpl extends EntityImpl {

    public Object getDataBaseValue(String attributeName){
        return getPostedAttribute(this.getAttributeIndexOf(attributeName));
    }
}

2. Create a View Row Impl (say SanViewRowImpl) base class as 

import oracle.jbo.server.AttributeDefImpl;
import oracle.jbo.server.ViewAttributeDefImpl;
import oracle.jbo.server.ViewRowImpl;

public class SanViewRowImpl extends ViewRowImpl {
  
    
    public boolean isEOBasedAttribute(String voAttributeName){
        ViewAttributeDefImpl voAttrDef = (ViewAttributeDefImpl)this.getViewDef().findAttributeDef(voAttributeName);
        byte attributeKind = voAttrDef.getAttributeKind();
        if(AttributeDefImpl.ATTR_PERSISTENT == attributeKind){
            return true;
        }
        else{
            return false;
        }
        
    }
    
    public boolean isTransientAttribute(String voAttributeName){
        ViewAttributeDefImpl voAttrDef = (ViewAttributeDefImpl)this.getViewDef().findAttributeDef(voAttributeName);
        byte attributeKind = voAttrDef.getAttributeKind();
        if(AttributeDefImpl.ATTR_TRANSIENT == attributeKind){
            return true;
        }
        else{
            return false;
        }
    }
    
    public Object getDatabaseValue(String voAttributeName){
        if(this.isEOBasedAttribute(voAttributeName)){
            ViewAttributeDefImpl voAttrDef = (ViewAttributeDefImpl)this.getViewDef().findAttributeDef(voAttributeName);
            AttributeDefImpl eoAttrDef = voAttrDef.getEntityAttributeDef();//voAttrDef.getReferenceAttribute()
            String entityAttributeName = eoAttrDef.getName();
            return ((SanEntityImpl)this.getEntity(this.getViewDef().getEntityIndex(voAttrDef.getEntityReference()))).getDataBaseValue(entityAttributeName);
            
        }
        else if(this.isTransientAttribute(voAttributeName)){
            return null;//Better if we can return initial value of attribute
        }
        else{
            return this.getAttribute(voAttributeName); 
        }
        
        
    }
    
    public boolean isAttributeCurrentlyChanged(String voAttributeName){
        Object dbValue = this.getDatabaseValue(voAttributeName);
        Object ctValue = this.getAttribute(voAttributeName);
        if(dbValue == null && ctValue == null){
            return false;
        }
        else if(dbValue == null && ctValue != null){
            return true;
        }
        else if(dbValue != null && ctValue == null){
            return true;
        }
        else if(dbValue.equals(ctValue)){
                return false;
        }
        else{
            return true;
        }
    }
   
}


3. Configure these classes as base classes in project properties



4. Test behavior of isAttributeChanged and isAttributeCurrentlyChanged

    Create EmployeeEO, EmployeeVO based on HR schema and add it in AM. Generate AMImpl and have below method in AM to verify


    public static void main(String[] args){
        String amDef = "com.san.adf.model.am.HRAppModule"; //Replace it with your AM
        String config = "HRAppModuleLocal";  //Replace it with your AM configuration
        HRAppModuleImpl am = (HRAppModuleImpl) Configuration.createRootApplicationModule(amDef, config);
        
        ViewObjectImpl empVO = am.getEmployeeVO();
        empVO.executeQuery();        
        SanViewRowImpl empRow = (SanViewRowImpl)empVO.first();
        
        
        String attrValue = null;
        String attributeName = "FirstName";
        if(empRow != null){
            
            
            attrValue = (String)empRow.getAttribute(attributeName);
            
            //When there is no change
            System.out.println("Without any change");
            System.out.println("isAttributeChanged: " + empRow.isAttributeChanged(attributeName));
            System.out.println("isAttributeCurrentlyChanged: " + empRow.isAttributeCurrentlyChanged(attributeName));
            
            //Change value of attribute and verify
            System.out.println("Changing attribute value to Sanjeev");
            empRow.setAttribute(attributeName, "Sanjeev");            
            System.out.println("isAttributeChanged: " + empRow.isAttributeChanged(attributeName));
            System.out.println("isAttributeCurrentlyChanged: " + empRow.isAttributeCurrentlyChanged(attributeName));
            
            
            //Restore value of attribute and verify
            System.out.println("Restoring attribute value");
            empRow.setAttribute(attributeName, attrValue);            
            System.out.println("isAttributeChanged: " + empRow.isAttributeChanged(attributeName));
            System.out.println("isAttributeCurrentlyChanged: " + empRow.isAttributeCurrentlyChanged(attributeName));
        }
        
        
        Configuration.releaseRootApplicationModule(am, true);
    }


If you run the AMImpl, output is something like this.

Without any change
isAttributeChanged: false
isAttributeCurrentlyChanged: false

Changing attribute value to Sanjeev
isAttributeChanged: true
isAttributeCurrentlyChanged: true

Restoring attribute value
isAttributeChanged: true
isAttributeCurrentlyChanged: false

We can see that after restoring value isAttributeCurrentlyChanged returns false, which means no change. 

Other utility method added in base class VORowImpl are 
isEOBasedAttribute: This method returns true if attribute is database driven.
isTransientAttribute: This method returns true if attribute is transient
getDatabaseValue: This attribute returns value of attribute from database.

Thats all.