Tuesday, April 23, 2013

Using ADF Client Side Listeners & Server Side Listeners to save panelSplitter position in a managed bean

In some cases where we find the framework supported events and handlers to be inadequate and need a custom event to be handled by the components. ADF Client side and server side listeners work in conjunction with each other to suffice such a requirement.

One such example is with panelSplitter component. Let us say a user wants to save the state of panelSplitter (splitter position) in session, then below solution would be handy.

The af:clientListener and af:serverListener tags are defined for a panelSplitter component as follows:

<af:panelSplitter id="ps1" label="MySplitter">
  <af:clientListener method="propertyChangeClientListener" type="propertyChange"/>
  <af:serverListener type="mySplitterCustomEvent"
                     method="#{mybean.handleSplitterPositionChange}"/>
</af:inputText>


On changing the splitter position through user action, the client listener invokes the below javascript method - propertyChangeClientListener(). That method indirectly invokes the server side listener by raising a particular event that the server side listener is waiting for. That event here is "mySplitterCustomEvent". This event is queued in AdfCustomEvent with the parameter as splitterPosition value and a configuration value "true" to make this immediate on server.

function propertyChangeClientListener(e) {
    var inputComp = AdfPage.PAGE.findComponent(e.getSource().getClientId());
    if(e.getPropertyName().toString() == "splitterPosition"){
      var newVal = e.getNewValue();
      AdfCustomEvent.queue(inputComp, "mySplitterCustomEvent",
                                 // Send one parameter
                                 {splitterPosition:newVal},
                                 // Make it "immediate" on the server
                                 true);
    }
}

Given below is the server side listener method. This method reads the value of the splitterPosition from the custom event received and stores the value in a managed bean "resizeBean".

public void handleSplitterPositionChange(ClientEvent event){
     ResizePersistenceBean resizeBean = null;
    try{
       //Use ADFUtil class posted in my earlier blogs
        resizeBean = (ResizePersistenceBean)ADFUtil.evaluateEL("#{resizePersistenceBean}");
       if(!event.getParameters().get("splitterPosition").toString().equals("")){
        resizeBean.setSplitterPosition((int)(Float.parseFloat(event.getParameters().get("splitterPosition").toString())));
       }
    }catch(Exception e){
       resizeBean.setSplitterPosition(200);
    }
}

Thursday, April 18, 2013

Remote Debugging of ADF web applications in Weblogic

We can debug our ADF web application by having the code pointed to a stand alone weblogic server.

To achieve that the startWeblogic.sh script that is present in <domain_dir>/bin/ , should be modified to keep the below entry, usually after java -version line

JAVA_OPTIONS="-Xdebug -Djava.compiler=NONE -Xnoagent -Xrunjdwp:transport=dt_socket,server=y,address=10171,suspend=n $JAVA_OPTIONS"

After the above change, the startWeblogic.sh is executed to bring up the server. Now the server listens at 10171 port for debugging.

Now, go to your adf application and choose the project that has the code you want to debug. Click on the Project Properties and edit the current run configuration with the host name and debug port details as shown in below pic.
Now save the project and right click on it and select "start Remote Debugger". This will make the project to attach to server at the debug port as mentioned. 

With this, when server is running the use case whose code is in the project configured, the control flows to the source code attached and can be debugged with debug points.

Thursday, April 4, 2013

Traversing ViewCriteria hierarchy

A ViewCriteria instance is an hierarchy of Criteria Rows and Criteria Items. Criteria Item is the smallest unit which contains an Operand, Operator and Value(Either Literal or Bind Variable).

A ViewCriteriaRow can have multipe ViewCriteriaItem instances and a ViewCriteria can have multiple ViewCriteriaRows. Let us assume appliedVC is an applied ViewCriteria to a ViewObject.


The below code snippet traverses the ViewCriteria and locates a particular ViewCriteriaItem and removes. Before removing it reads the value and stores it in a variable.



            boolean found = false;
            while (appliedVC.hasNext() && !found) {
                vcr = (ViewCriteriaRow)appliedVC.next();
                if (vcr != null) {
                   ViewCriteriaItem[] vcis = vcr.getCriteriaItemArray();
                   if (vcis != null && vcis.length > 0) {
                       for (int j = 0; j < vcis.length && !found; j++) {
                           ViewCriteriaItem vci = vcis[j];
                           if(vci!= null && vci.getAttributeDef()!=null && vci.getAttributeDef().getName().equals("MyFlag")){
                                System.out.println("****"+vci.getAttributeDef().getName()+"***"+vci.getValue());
                                if(vci.getValue()!=null){
                                    myFlagVal = vci.getValue().toString();
                                    vcr.removeCriteriaItem("MasterOrgFlag", vci);
                                    found=true;
                                }
                           }
                       }
                   }
                }
            }

Replacing a particular ViewCriteriaItem with custom where condition

Sometimes we may come across a situation where we want to have a field in Search Component in UI but don't want the corresponding view criteria item to be part of the where clause generated for the ViewObject. This requirement generally comes when the value set for the search field in UI is used to decide which custom where condition to be added instead of the search field itself.

This requirement makes more sense when a ViewObject is read-only based on a sql query and you want the query execution mode for the ViewCriteria to be Database.

You need to override the getCriteriaItemClause() method of the ViwObject

The below code snippet looks for the search field "ManagerOnly" which has two possible values "Y"/"N". If the Value is "Y", a custom where condition has to be returned else nothing, which means there won't be any clause generated for the corresponding criteria item.

(Assumption: There is Emp schema where Emp details are stored in Employee table. Manager is highest in hierarchy and the manager_id value for manager will be its own employee id.)

 @Override
    public String getCriteriaItemClause(ViewCriteriaItem vci) {
      if (vci.getAttributeDef().getName().equals("ManagerOnly") &&      vci.getViewCriteria().getName().contains("MyEmpViewCriteria")&& vci.getValue().equals("Y")) {
        return "EMPLOYEE_ID=MANAGER_ID";
      } else { //If the attribute is not "ManagerOnly" retain the default behavior.
        return super.getCriteriaItemClause(vci);
      }
    }

Adding a where clause to a View Object through dummy View Criteria

This can be done by creating a dummy view criteria in View object without any criteria items in it. Then in the code like prepareRowSetForQuery() method of View Object the following can be done to achieve that.

        ViewCriteria vc = this.getViewCriteria("getMyItems");
       
        CriteriaClauses c = new CriteriaClauses();
        c.setClauseForQuery("OrgId IS NOT NULL");
        vc.setClauses(c);

The above will add where clause to the View object. In case multiple view criterias are used this will retain them as well while appending the where clause generated by it.

Programmatically applying View Criteria

A ViewObject can be defined with multiple ViewCriterias having multiple Criteria Items within it.
For a ViewObject consumer to execute it with a specific ViewCriteria programmatically, below is the code assuming "am" is the Application Module instance.

 ViewObject pVO = _am.findViewObject("OrgOrganizationDefinitionsSecuredP1");
  if(pVO != null){
     ViewCriteriaManager vcm = pVO.getViewCriteriaManager();
     ViewCriteria vc =   vcm.getViewCriteria("myCriteria");
     ViewCriteria dsvc  =  vcm.getViewCriteria("getDSBackedOrgs");
           
      ViewCriteriaRow vcr = vc.createViewCriteriaRow();
      vcr.setAttribute("MasterOrgFlag", "= Y");
      vcr.setConjunction(ViewCriteriaRow.VC_CONJ_AND);
      vc.add(vcr);

     ViewCriteriaRow vcr1 = (ViewCriteriaRow)vc.createViewCriteriaRow();
     vcr1.setAttribute("OrganizationCode", "= V1");
     vcr1.setConjunction(ViewCriteriaRow.VC_CONJ_AND);
     vc.add(vcr1);

     pVO.applyViewCriteria(vc);
     pVO.applyViewCriteria(dsvc,true);
     pVO.executeQuery();   
  }  


The above piece of code programmatically applies a view criteria of my choice among the list of view criterias defined for the view object. It programmatically adds criteria rows to it. Each criteria row can have multiple criteria items and each such item corresponds to one where condition in SQL Where clause. The setAttribute method on ViewCriteriaRow defines a criteria item here.

Another important finding in the above code is the way to apply multiple criterias to a view object. The applyViewCriteria(<ViewCriteriaName>) method clears of the existing list of applied view criterias and applies the current one. The applyViewCriteria(<ViewCriteriaName>, true) appends the current view criteria to the existing list.







Wednesday, March 20, 2013

An ADF Util class that has various Utility methods

package test.util;

import com.sun.faces.mgbean.ManagedBeanCreationException;

import java.util.HashMap;
import java.util.Map;

import javax.el.ELContext;
import javax.el.Expression;
import javax.el.ExpressionFactory;
import javax.el.MethodExpression;
import javax.el.ValueExpression;

import javax.faces.application.FacesMessage;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;

import javax.faces.event.PhaseId;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpSession;

import oracle.adf.model.BindingContext;
import oracle.adf.model.DataControlFrame;
import oracle.adf.model.binding.DCBindingContainer;
import oracle.adf.model.binding.DCDataControl;
import oracle.adf.view.rich.component.rich.RichPopup;
import oracle.adf.view.rich.component.rich.fragment.RichRegion;
import oracle.adf.view.rich.component.rich.layout.RichPanelSplitter;
import oracle.adf.view.rich.context.AdfFacesContext;

import org.apache.myfaces.trinidad.render.ExtendedRenderKitService;
import org.apache.myfaces.trinidad.util.Service;

/**
 * Provides various utility methods that are handy to
 * have around when working with ADF.
 */
public class ADFUtil {
    /**
     * When a bounded task flow manages a transaction (marked as
     * requires-transaction, requires-new-transaction, or requires-
     * existing-transaction), then the task flow must issue any
     * commits or rollbacks that are needed.
     * This is essentially to keep the state of the transaction that
     * the task flow understands in synch with the state of the
     * transaction in the ADFbc layer.
     * Use this method to issue a commit in the middle of a task flow
     * while staying in the task flow.
     */
    public static void saveAndContinue() {
        Map sessionMap =
            FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
        BindingContext context =
            (BindingContext)sessionMap.get(BindingContext.CONTEXT_ID);
        String currentFrameName = context.getCurrentDataControlFrame();
        DataControlFrame dcFrame =
            context.findDataControlFrame(currentFrameName);
        dcFrame.commit();
        dcFrame.beginTransaction(null);
    }

    /**
     * Programmatic evaluation of EL.
     *
     * @param el EL to evaluate
     * @return Result of the evaluation
     */
    public static Object evaluateEL(String el) {
        FacesContext facesContext = FacesContext.getCurrentInstance();
        ELContext elContext = facesContext.getELContext();
        ExpressionFactory expressionFactory =
            facesContext.getApplication().getExpressionFactory();
        ValueExpression exp =
            expressionFactory.createValueExpression(elContext, el,
                                                    Object.class);
        if(exp == null || elContext == null){
            return null;
        }
        Object obj = null;
        try{
            obj = exp.getValue(elContext);
        }catch(ManagedBeanCreationException ex){
           
        }
        return obj;
    }

    public static String getLocaleValue(String el,String code){
            Map map = (Map)evaluateEL(el);
            if(map != null){
                if(map.get(code) != null)
                    return (String)map.get(code);
                else
                    return code;
            }
            return "";
        }
    /**
     * Programmatic invocation of a method that an EL evaluates to.
     * The method must not take any parameters.
     * @param el EL of the method to invoke
     * @return Object that the method returns
     */
    public static Object invokeEL(String el) {
        return invokeEL(el, new Class[0], new Object[0]);
    }

    /**
     * Programmatic invocation of a method that an EL evaluates to.
     *
     * @param el EL of the method to invoke
     * @param paramType Class defining the type of the
     * parameter
     * @param param Object defining the value of the
     * parameter
     * @return Object that the method returns
     */
    public static Object invokeEL(String el, Class paramType, Object param) {
        return invokeEL(el, new Class[] { paramType }, new Object[] { param });
    }


    /**
     * Programmatic invocation of a method that an EL evaluates to.
     *
     * @param el EL of the method to invoke
     * @param paramTypes Array of Class defining the types of the
     * parameters
     * @param params Array of Object defining the values of the
     * parametrs
     * @return Object that the method returns
     */
    public static Object invokeEL(String el, Class[] paramTypes,
                                  Object[] params) {
        FacesContext facesContext = FacesContext.getCurrentInstance();
        ELContext elContext = facesContext.getELContext();
        ExpressionFactory expressionFactory =
            facesContext.getApplication().getExpressionFactory();
        MethodExpression exp =
            expressionFactory.createMethodExpression(elContext, el,
                                                     Object.class, paramTypes);
        return exp.invoke(elContext, params);
    }


    /**
     * Sets a value into an EL object. Provides similar
     * functionality to
     * the &lt;af:setActionListener&gt; tag, except the
     * <code>from</code> is
     * not an EL. You can get similar behavior by using the
     * following...<br>
     * <code>setEL(<b>to</b>, evaluateEL(<b>from</b>))</code>
     *
     * @param el EL object to assign a value
     * @param val Value to assign
     */
    public static void setEL(String el, Object val) {
        FacesContext facesContext = FacesContext.getCurrentInstance();
        ELContext elContext = facesContext.getELContext();
        ExpressionFactory expressionFactory =
            facesContext.getApplication().getExpressionFactory();
        ValueExpression exp =
            expressionFactory.createValueExpression(elContext, el,
                                                    Object.class);
        exp.setValue(elContext, val);
    }
   
    public static String errorDialog(String errorMessage)
    {       
        FacesContext context = FacesContext.getCurrentInstance();
        ((HttpSession)context.getExternalContext().getSession(false)).setAttribute("errorMessage", errorMessage);
        ViewHandler viewHandler = context.getApplication().getViewHandler();
        UIViewRoot dialog = viewHandler.createView(context, "/Error.jspx");         
        HashMap properties = new HashMap();
        properties.put("width", new Integer(250));
        properties.put("height", new Integer(150));     
        AdfFacesContext afContext = AdfFacesContext.getCurrentInstance();       
        afContext.launchDialog(dialog,
                null, // not launched from any component
                    null, // no particular parameters                                       
                        true,properties); // show it in a dialog
        context.renderResponse();
        return "";
    }
   
    /**
     *Get the ADF datacontrol reference given the control name
     * @param controlName data control name as given in data bindings file
     * @return
     */
    public static DCDataControl findDataControl(String controlName){
        DCBindingContainer context = (DCBindingContainer)ADFUtil.evaluateEL("#{bindings}");
        DCDataControl dcf = context.findDataControl(controlName);
        return dcf;
    }  
   
    /**
     * Initiates control flow in a task flow region.
     * @param region - RichRegion that represents taskflow.
     * @param outComeEl - action outcome, based on whic control flows
     */
    public static void performControlFlow(RichRegion region, String outComeEl){
        FacesContext ctx = FacesContext.getCurrentInstance();
        ExpressionFactory f = ctx.getApplication().getExpressionFactory();
        ELContext elctx = ctx.getELContext();
        MethodExpression m = f.createMethodExpression(elctx,outComeEl, String.class, new Class[]{});           
        region.queueActionEventInRegion(m,null,null,false,-1,-1,PhaseId.ANY_PHASE);
    }  
   
    public static HttpServletRequest getHttpServletRequest(){
        return ((HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest());
    }
   
    public static HttpSession getHttpSession(){
        return ((HttpSession)FacesContext.getCurrentInstance().getExternalContext().getSession(false));
    }
   
    /**
     * Returns the Binding Container
     * @return
     */
    public static DCBindingContainer getBindingContainer(){
        return (DCBindingContainer)evaluateEL("#{bindings}");
    }
   
    /**
     * Renders Faces message showing some validation error message.
     * @param id
     * @param message
     */
    public static void addFacesMessage(String id, FacesMessage message){
        FacesContext.getCurrentInstance().addMessage(id, message);
        FacesContext.getCurrentInstance().renderResponse();
    }
   
    public static void addFacesMessage(String clientId,javax.faces.application.FacesMessage.Severity severity,String localizedText){
        FacesContext.getCurrentInstance().addMessage(clientId, new FacesMessage(severity, "",localizedText));
        FacesContext.getCurrentInstance().renderResponse();
    }
   
    public static String showError(String clientId,String errorMessage) {       
       
        FacesContext ctx = FacesContext.getCurrentInstance();      
        StringBuilder javaScriptPopup =
            new StringBuilder("var popObj=AdfPage.PAGE.findComponent('" +
                              clientId + "');");
        javaScriptPopup.append("if(popObj != null){var errorField = popObj.findComponent('errorMessage');if(errorField != null)");
        javaScriptPopup.append("{errorField.setValue('");       
        javaScriptPopup.append(errorMessage);               
        javaScriptPopup.append("')}");
        javaScriptPopup.append("popObj.show();}");       
       
        ExtendedRenderKitService erks =
            Service.getRenderKitService(ctx, ExtendedRenderKitService.class);
        erks.addScript(ctx, javaScriptPopup.toString());
        ctx.renderResponse();
        return "";
    }
   
    public static void showPopup(String clientId){
        FacesContext ctx = FacesContext.getCurrentInstance(); 
        StringBuilder javaScriptPopup =
            new StringBuilder("var popObj=AdfPage.PAGE.findComponent('" +
                              clientId + "');");       
        javaScriptPopup.append("popObj.show();");      
       
        ExtendedRenderKitService erks =
            Service.getRenderKitService(ctx, ExtendedRenderKitService.class);
        erks.addScript(ctx, javaScriptPopup.toString());
        ctx.renderResponse();
    }
   
    public static void expandSpilter(RichPanelSplitter splitter){
        String clientId =splitter.getClientId(FacesContext.getCurrentInstance());
        FacesContext facesContext = FacesContext.getCurrentInstance();
        ExtendedRenderKitService service = org.apache.myfaces.trinidad.util.Service.getRenderKitService(facesContext, ExtendedRenderKitService.class);
        StringBuilder script = new StringBuilder();       
        script.append("var panelSpliter = AdfPage.PAGE.findComponent('").append(clientId).append("');\n");       
        script.append("panelSpliter.setCollapsed('');\n");       
        service.addScript(facesContext, script.toString());
    }
   
    public static void collapseSplitter(RichPanelSplitter splitter){
        String clientId =splitter.getClientId(FacesContext.getCurrentInstance());
        FacesContext facesContext = FacesContext.getCurrentInstance();
        ExtendedRenderKitService service = org.apache.myfaces.trinidad.util.Service.getRenderKitService(facesContext, ExtendedRenderKitService.class);
        StringBuilder script = new StringBuilder();       
        script.append("var panelSpliter = AdfPage.PAGE.findComponent('").append(clientId).append("');\n");       
        script.append("panelSpliter.setCollapsed(true);\n");       
        service.addScript(facesContext, script.toString());
    }
   
    public static void closwBrowser(){
        FacesContext facesContext = FacesContext.getCurrentInstance();          
        ExtendedRenderKitService erks = Service.getRenderKitService(facesContext, ExtendedRenderKitService.class);
        StringBuilder sb=new StringBuilder("window.close();");
        erks.addScript(facesContext,sb.toString());
        facesContext.renderResponse();
    }
   
}