Wednesday, October 27, 2010

ADF Exact custom filters

Step 1)
Add your custom filter to the column filter facet.
  <f:facet name="filter">
    <af:selectBooleanCheckbox value="#{backingBean.customfilter}"         id="id99"/>
  </f:facet>

Step 2) Create the getters and setters in your backing bean
    public Boolean getCustomfilter(){
        Boolean returnobj = null;
        Object filterValue = super.getFilterValue("AttributeName");
        if (filterValue != null){
            returnobj = ("Y".equalsIgnoreCase(filterValue.toString())) ? Boolean.TRUE : Boolean.FALSE;
        }
        return returnobj;
    }
 
    public void setCustomfilter(Boolean deletefilter){
        String value = filter == null ? null : (deletefilter) ? "Y" : "N";
        super.setFilterValue(value, "AttributeName");
    }

NOTE: Dates

Filtered Dates with timestamps in ADF dont work very nicely if you just want to filter by date. So your can modify the date filters as above and here is some code to help with the process. (Note you will need to override the queryListener)
just call this from your listener.

ArrayList fieldNames = new ArrayList();
fieldNames.add("DateAttr");
queryWithDateFilter(
queryEvent, vo, "#{bindings.CustomVOQuery.processQuery}", fieldNames);


public void queryWithDateFilter(
  QueryEvent queryEvent, ViewObject vo, 
  String mappedQueryEL, List fieldNames){

  FilterableQueryDescriptor fqd = 
    (FilterableQueryDescriptor)queryEvent.getDescriptor();
  Map map = fqd.getFilterCriteria();

  if (fieldNames != null){
    Map selectedValues = getSelectedValues(fieldNames, map);
    StringBuilder whereClauseSql = new StringBuilder();
    for (String fieldKey : fieldNames){
      String filterKey = "dateFilter_" + fieldKey;
      Object selectedValue = selectedValues.get(fieldKey);
      if (selectedValue != null){
        fqd.getFilterCriteria().put(fieldKey, null);
        AttributeDef def = getAttributeDefForKeyName(vo, fieldKey);
        whereClauseSql.append(
          "TRUNC(" + def.getColumnNameForQuery() + ", 'DDD') = :" + filterKey);
        vo.defineNamedWhereClauseParam(filterKey, convertDate(selectedValue), null);
        vo.ensureVariableManager().setVariableValue(
          filterKey, convertDate(selectedValue));
      } else {
        if (hasParam(filterKey, vo.getNamedWhereClauseParams())){
        vo.removeNamedWhereClauseParam(filterKey);
      }
     }
   }
   if (whereClauseSql.length() > 0){
     vo.setWhereClause(whereClauseSql.toString());
    } else {
      vo.setWhereClause(whereClause);
    }
    JSFUtils.invokeMethodExpression(
      mappedQueryEL, Object.class, QueryEvent.class, queryEvent);
    for (String fieldKey : fieldNames){
      Object selectedValue = selectedValues.get(fieldKey);
      fqd.getFilterCriteria().put(fieldKey, selectedValue);
    }
  } else {
    JSFUtils.invokeMethodExpression(
      mappedQueryEL, Object.class, QueryEvent.class, queryEvent);
  }
}

protected Map getSelectedValues(
  List fieldNames, Map mapValues){
  Map returnMap = new HashMap();
  Set set = mapValues.keySet();
  for (String key : set){
    if (fieldNames.contains(key)){
      returnMap.put(key, mapValues.get(key));
    }
  }
  return returnMap;
}

private Object convertDate(Object selectedValue){
   if (selectedValue != null && selectedValue instanceof java.util.Date){
     java.util.Date dateValue = (java.util.Date)selectedValue;
     return new Date(new java.sql.Date(dateValue.getTime()));
   } else {
    return selectedValue;
  }
}

protected AttributeDef getAttributeDefForKeyName(
  ViewObject vo, String fieldKey){
  AttributeDef def = null;
  AttributeDef[] defs = vo.getAttributeDefs();
  if (defs != null){
    for (AttributeDef currentDef : defs){
      if (currentDef.getName().equalsIgnoreCase(fieldKey)){
        return currentDef;
      }
    }
  }
  return def;
}

ADF VO criteria performance problem

One of our screens backed by a huge table was slow. (+1 10 seconds just for the query alone). And here is what I found out:

Our query with a criteria to filter the tablename and key < 1 second
SELECT
a.ele_name AS ELEMENT_NAME, a.pk AS PKEY, a.audit_result AS AUDIT_RESULT
FROM audit a, mds_users u
WHERE a.user = u.code
AND a.ele_name = :tablename
AND a.pk = :key

ADF converted query 12 seconds looks something like this:
SELECT * FROM (SELECT
a.ele_name AS ELEMENT_NAME, a.pk AS PKEY, a.audit_result AS AUDIT_RESULT
FROM audit a, users u
WHERE a.user = u.code) QRSLT WHERE ( ( ( (UPPER(PKEY) = UPPER(:key) ) AND (UPPER(ELEMENT_NAME) = UPPER(:tablename) )) ) ) ORDER BY AUDIT_DATE DESC

So I remove the UPPER from the query and all was good. Note to self always check this or you will suffer. (Ignore case check box in the criteria if you are looking for this)

Tuesday, October 19, 2010

Auto Submit button for page fragements

If your region is encased in a form you will have no access to the defaultCommand on your af:form.

Try adding a subform to your fragement (jsff) and use its defaultCommand - note this will affect your naming. This should also work for popup buttons.

ie. after your <jsp:root
<af:subform defaultCommand="submitButton" id="s3">
   ... your code
</af:subform>

Monday, October 18, 2010

JDBC oracle Boolean problem.

? := SYS.SQLJUTL.BOOL2INT(boolReturnFunction(?,?))

And then map the result to an int.

Tuesday, October 12, 2010

ADF tree menu code

We have a tree menu that works of a hierarchical structure in the DB. Note you must have seperate iterators for the tree and the view. It will disclose and select an item in your tree based on a selection. This can be modified in various ways to get your trees jumping through hoops.

So here is the code so I dont loose it:
<af:tree value="#{bindings.TreeRootMenuVO2.treeModel}" var="node"
    selectionListener="#{pageFlowScope.TreeBean.selectionListener}"
    rowSelection="single" id="t1"
    binding="#{pageFlowScope.TreeBean.menuTree}"
    contentDelivery="immediate" fetchSize="500">
  <f:facet name="nodeStamp">
    <af:outputText value="#{node}" id="ot1"/>
  </f:facet>
</af:tree>

  <af:commandToolbarButton text="Find Node" id="cb5"
  actionListener="#{pageFlowScope.TreeBean.findInTree}"/>

  <af:commandToolbarButton text="JS Test" id="cb15">
    <af:clientListener method="selectNode" type="click"/>
  </af:commandToolbarButton>
Java code for the selection of an item.
public void selectionListener(SelectionEvent selectionEvent){
  //Invoke standard tree selection listener
  invokeMethod("#{bindings.TreeRootMenuVO2.treeModel.makeCurrent}",
    Object.class, new Class[]{ SelectionEvent.class }, 
    new Object[]{ selectionEvent });
  RichTree rt = this.getMenuTree();
  RowKeySet rks = rt.getSelectedRowKeys();
  BigDecimal menuId = null;

  for (Object facesTreeRowKey : rks){
    rt.setRowKey(facesTreeRowKey);
    JUCtrlHierNodeBinding root = (JUCtrlHierNodeBinding)rt.getRowData();
    menuId = (BigDecimal)root.getRow().getAttribute("MenuId");
  }

  DCIteratorBinding iter = 
    getBindingContainer().findIteratorBinding("MenuVO1Iterator");
  //this method just applies a view criteria containing just the pk to the 
  //iterator then calls  
  //vo.getViewCriteriaManager().removeApplyViewCriteriaName(criteriaName);
  applyCriteriaAndQuery(iter, "MenuVOPKCriteria", createMenuParams(menuId));
  AdfFacesContext.getCurrentInstance().addPartialTarget(formBind);
}

Java code for the find in tree method.
public void findInTree(ActionEvent actionEvent) throws Exception{
  // find current row in the menu iterator
  BigDecimal menuId = retrieveCurrentRowId();
  if (menuId != null){
  JUCtrlHierBinding tree = (JUCtrlHierBinding)
    ((CollectionModel)getMenuTree().getValue()).getWrappedData();
  // Method that gets the hieracical tree for the selected item
  List keyPath = getBranchKeys(menuId);
  if (keyPath != null){
    JUCtrlHierNodeBinding node = tree.findNodeByKeyPath(keyPath);
    List selectedKeyList = node.getKeyPath();
    RowKeySet sRowKeys = menuTree.getSelectedRowKeys();
    sRowKeys.clear();
    sRowKeys.add(selectedKeyList);
    List disclosedKeyList = new ArrayList();
    buildDisclosedRows(node, disclosedKeyList);
    RowKeySet dRowKeys = menuTree.getDisclosedRowKeys();
    dRowKeys.clear();
    if (disclosedKeyList != null && disclosedKeyList.size() > 0){
      for (Object disclosedItemList : disclosedKeyList){
        dRowKeys.add(disclosedItemList);
      }
    }
    AdfFacesContext.getCurrentInstance().addPartialTarget(menuTree);
    }
  }
}

    private void buildDisclosedRows(JUCtrlHierNodeBinding node, List keyList){
        JUCtrlHierNodeBinding parent = node.getParent();
        if (parent != null && parent.getKeyPath() != null){
            buildDisclosedRows(parent, keyList);
        }
        keyList.add(node.getKeyPath());
    }

And just for interest sake some javascript code to work with the menu that does very little but should give you an idea.

<af:resource type="javascript">
  function selectNode(evt) {
    //Get instance of AdfRichTree.js
    var tree = AdfPage.PAGE.findComponent("t1");
    var srks = tree.getSelectedRowKeys();
    var firstRowKey;
    for (rowKey in srks){
      firstRowKey  = rowKey;
      alert("idx:::" + firstRowKey);
      break;
    }

    if (tree.isPathExpanded(firstRowKey)){
      tree.setDisclosedRowKey(firstRowKey,false);
      tree.setDisclosedRowKey(7,true);
      var keys = new Array[1];
      keys[0] = 7;
      tree.getSelectedRowKeys(keys);
      //tree.setValue(7);
    }
  }
</af:resource>
If you need any other code that I have left out just shout.

Monday, October 11, 2010

Traversing a hierarchical oracle tree in SQL backwards

So I have a menu table with a parent child relationship menu_id, menu_name, parent_id and I want to retrieve the whole branch that Menu Child E is in. ie all its ancestors. The result should be ordered from the root.

So given:
Root (id:0 parent_id: null)
  Menu Parent A(id:1 parent_id: 0)
    Menu Child B(id:2 parent_id: 1)
  Menu Parent C(id:3 parent_id: 0)
    Menu Child D(id:4 parent_id: 3)
    Menu Child E(id:5 parent_id: 3)

I want:
Root (id:0 parent_id: null)
  Menu Parent C(id:3 parent_id: 0)
    Menu Child E(id:5 parent_id: 3)

select
m.menu_id, m.menu_name, m.parent_id
from
MENU m
start with
m.menu_id = 5
connect by
prior m.parent_id = m.menu_id
ORDER BY LEVEL DESC;

Random helpful code

adfFacesContext.isPartialRequest(fctx); //needed for a phase listener I had

Will add more here when I think of it...

Thursday, October 7, 2010

rangeChangeListener not firing in ADF table

Apparently this doent work in ADF so you cant really use the table outside of the adf framework.

Readonly View Objects and Disconnect Application Module Upon Release

Once setting Disconnect Application Module Upon Release a few of our screens with tables that depend on SQL query backed read only view objects stop functioning correctley. (Som flashed one refreshed the table to the top when the down arrow was used etc)

Creating keys for these view objects fixed the problems.

Wednesday, October 6, 2010

Before I forget - debugging in ADF

Right click on you ViewController project.
Select Project Properties
Run/Debug/Profile
Click on Edit on your Configuration
Unser java options Add: -Djbo.debugoutput=console -Djbo.jdbc.trace=true

Monday, October 4, 2010

The DocumentChange is not configured to be allowed for the component:

<FilteredPersistenceChangeManager><_addDocumentChangeImpl> The DocumentChange is not configured to be allowed for the component: RichTable[org.apache.myfaces.trinidad.component.UIXTable$RowKeyFacesBeanWrapper@fdf42c, id=audTab]

This is what I understand this message to mean:
If MDS is configured a request is send to the FilteredPersistenceChangeManager wich then does a check to see if the component is configured.

If the change is not configured then the change will then be written to the users session and you will get the above message in your logs.

To configure the MDS stuff in your adf application :
1) Open adf-config.xml - located in Application Resources < ADF META-INF dir.
2) On the View tab click the green plus button and select the component that the error is complaining about.
3) Configure as needed.