23 Sep 2012

Dynamic view criterias from where clauses

Let's consider a use-case when a system administrator or a business administrator or even a user can define some user-filters. These user filters are going to be stored in some storage, for example a database, and at run-time an end user can apply these filters to their data in UI. These filters contain just simple Where SQL clauses. For example, we have in the application some entity "Boats" and it has some predefined user filters:

It would be cool to dynamically create and add View Criterias to the VO definition and to work with them in a usual way. For example, we could use standard af:query component, where a user can select a criteria from the list and apply it to the data in a table. But the question is: how to create View Criterias having only simple where clause strings?
A View Criteria consists of view criteria rows, and a view criteria row consists of view criteria items, and a view criteria item consists of a view object attribute, an operation type like equal, less, like or whatever and an operand, literal or bind variable. The framework at run-time generates appropriate where clauses from these criteria items and applies them to the SQL query. But we don’t have all these cool things – an attribute, an operation type and an operand, we just have a where clause string and nothing else.
The trick is to put this string into the value property of the view criteria item and override a view object method getCriteriaItemClause responsible for generating a where clause from a view criteria item:

   @Override
  public String getCriteriaItemClause(ViewCriteriaItem crieriaItem) {
      return (String) crieriaItem.getValue(); 
   }  

The method is just going to return back a criteria item’s value. And we can add a method to the custom ViewDefImpl class:

    private void createViewCriterias(RowIterator userFilters){
      while (userFilters.hasNext()) {
        VAgileEntityFilterRowImpl userFilter = (VAgileEntityFilterRowImpl) userFilters.next();
        //Create a View Criteria
        ViewCriteriaImpl viewCriteria =  (ViewCriteriaImpl) createViewCriteria();
        viewCriteria.setName(userFilter.getName());
        
        //Create a View Criteria Row
        ViewCriteriaRow vcr = viewCriteria.createViewCriteriaRow();
        
        //Get the first attribute from the VO's attribute list
        String firstAttributeName = getAttributeDef(0).getName();
        //And create a View Criteria Item for this attribute
        ViewCriteriaItem vci = new ViewCriteriaItem(firstAttributeName, vcr);
        
        //Set the Where clause string as a value of the 
        //View Criteria Item instance
        vci.setValue(userFilter.getWhereclause());
        
        //Add the View Criteria Item instance to the View Criteria Row
        vcr.addCriteriaItem(firstAttributeName, vci);        
        
        //Add the View Criteria Row instance to the View Criteria
        viewCriteria.add(vcr);
            
        //Add the View Criteria instance to the View Definition
        putViewCriteria(viewCriteria.getName(), viewCriteria);
        
      }
    }
 
A View Criteria Item can be created for some VO's attribute only. But we’ve overridden generating of a where clause from a view criteria item. So, it doesn't matter for which particular attribute it's going to be created.  Therefore, we can just take the first attribute from the list and pass it to the ViewCriteriaItem constructor. Actually, that's it. And now we can work with these View Criterias in a usual way.

22 Sep 2012

Dynamic iterator binding and method action call

In my previous post I mentioned that we can use dynamic binding approach in an effort to get the VO instance name evaluated dynamically at run-time. So, in the PageDef file we can have the following definition:


<iterator Binds="#{someViewObjectName}" RangeSize="25"   
               DataControl="AgileDataModelServiceDataControl"  
               id="VAgileIterator"/>



And EL #{someViewObjectName} is going to be evaluated as a name of some view object instance in the AgileDataModelService application module. And let's assume that any VO instance, which name is evaluated by the EL expression, inherits some parent view object VAgile. And its implementation class VAgileImpl contains some public method returning a String:

    public String getSomeString() {
        return ...;
    }


We're going to publish this method in VO's client interface and make it invokable from the UI via  binding layer. So, we're going to define a methodAction in the PageDef file in order to invoke the method for the particular VO instance, which name is going to be evaluated by the #{someViewObjectName} EL expression:

<methodAction id="getSomeString" RequiresUpdateModel="true"
              Action="invokeMethod" MethodName="getSomeString"
              IsViewObjectMethod="true"
              DataControl="AgileMetaDataModelServiceDataControl"
              InstanceName="???"
              ReturnName="data.AgileMetaDataModelServiceDataControl.methodResults.getSomeString_AgileMetaDataModelServiceDataControl_VAgile_getSomeString_result"/>

But a methodAction definition has an InstanceName attribute, supposed to contain a name of the particular VO instance. Since we're using the dynamic iterator binding approach, we don't know the instance name, it's going to be evaluated at run time by the EL expression. Unfortunately we can't use EL expressions in the Instance attribute. The workaround is to use an iterator bindings VAgileIterator to get the instance of the VO. So we can do the following:

<methodAction id="getSomeString" RequiresUpdateModel="true"
              Action="invokeMethod" MethodName="getSomeString"
              IsViewObjectMethod="false"
              DataControl="AgileMetaDataModelServiceDataControl"
              InstanceName="bindings.VAgileIterator.viewObject"
              ReturnName="data.AgileMetaDataModelServiceDataControl.methodResults.getSomeString_AgileMetaDataModelServiceDataControl_VAgile_getSomeString_result"/>

Have a note, that we used bindings.VAgileIterator.viewObject in the InstanceName attribute and the IsViewObjectMethod attribute is false. Actually, that's it. It works.