woensdag 13 november 2013

How to run your ADF 11.1.x application under IE 11: a workaround

Introduction

Recently Microsoft has released its new version 11 of Internet Explorer. When developing ADF-applications the most obvious question is of course: will my application still work? The short answer is: NO, you have to wait until Oracle brings out a new patch. When you try to run your application, you will get a popup-message about the fact that you are using a non-supported browser version, and your application will not work the way you expect. Running the browser in compatibility-mode might work in some situations, but users also get annoying popups, and in many cases the layout and the behaviour of the application is not the way it should be.

But there is a workaround. We introduced this workaround in an  ADF 11.1.1.6 application that also had to run under IE10. This is a non-certified combination and running it results in strange client-behavior of the application. E.g. the movement of a splitter results in a complete blank screen as long as you hold the mouse down. As probably in many production environments, we are not (yet) able to migrate to ADF 11.1.1.7, and since the application runs in many different sites, we are not able to control the browser versions. We found a solution that works pretty good. And the good news is: it also seems to work for Internet Explorer 11!
A sample of the solution can be downloaded from IEFilterDemo.zip .


The problem

When looking for a solution I found some examples that were based on the idea of adding a new header to the HttpServletResponse. E.g.

       HttpServletResponse response = (HttpServletResponse)ectx.getResponse();  
       response.addHeader("X-UA-Compatible", "IE=EmulateIE9");  

However, this does not work. When ADF receive a request, one of the things that happen is that, based on the request-headers, it determines the user-agent. If this agent is an MSIE agent, the first thing that ADF writes in the response is an meta-tag that contains the browser version. So a request from IE 10 will always result in a response containing an IE 10 meta-tag:


 <html class="p_AFMaximized" lang="nl-NL" dir="ltr">  
  <head>  
    <meta http-equiv="X-UA-Compatible" content="IE=10.0">  
    ....  

Any attempt to add your own response-header to make the IE browser behave like an older version will be ignored.

IE 11 makes it even worse. This version doesn't send a user-agent header containing a reference to MSIE. It sends a header that should make applications believe the request comes from a 'Gecko-like' browser. The idea is that IE 11 is compatible with these browsers.


user-agent header from IE 11:
Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko


The solution

The solution is quite simple: we have to make ADF believe that the request comes from an older version of IE, so in return ADF will write the correct meta-tag into the response, and IE will act accordingly.

This can be achieved by creating and adding a custom filter. The filter determines the user-agent, and in case of IE 10 or 11, it wraps the request in our own version of a HttpServletRequestWrapper. The WrapperRequest has a method getHeader() in which we control what string is returned when the "user-agent" header is requested.
When we place our filter in the top of the filter-chain, the request that ADF receives, is our WrappedRequest. When ADF tries to determine the user-agent, it will receive the answer given by our WrapperRequest, which makes ADF think we are using an IE 9 or IE 10 browser.

 package view;  
   
 import java.io.IOException;  
   
 import javax.servlet.Filter;  
 import javax.servlet.FilterChain;  
 import javax.servlet.FilterConfig;  
 import javax.servlet.ServletException;  
 import javax.servlet.ServletRequest;  
 import javax.servlet.ServletResponse;  
 import javax.servlet.http.HttpServletRequest;  
 import javax.servlet.http.HttpServletRequestWrapper;  
   
   
 public class IECompatibleFilter implements Filter {  
   public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {  
   
     String userAgentStr = ((HttpServletRequest)request).getHeader("user-agent");  
     // Check to see if we have to do with a IE request. If so, we return a wrapped request.  
     if (userAgentStr != null && (userAgentStr.contains("MSIE 1") || 
                                     userAgentStr.contains("Trident"))) {  
       ServletRequest wrappedRequest = new WrapperRequest((HttpServletRequest)request);  
       chain.doFilter(wrappedRequest, response);  
     } else {  
       chain.doFilter(request, response);  
     }  
   }  
   
   public void destroy() {  
   }  
   
   public void init(FilterConfig arg0) throws ServletException {  
   }  
   
   private class WrapperRequest extends HttpServletRequestWrapper {  
     public WrapperRequest(HttpServletRequest request) {  
       super(request);  
     }  
   
     public String getHeader(String name) {  
       // IE 10: replace 'MSIE 10.x' into 'MSIE 9.x' for ADF 11.1.1.6 and below  
       HttpServletRequest request = (HttpServletRequest)getRequest();  
       if ("user-agent".equalsIgnoreCase(name) && request.getHeader("user-agent").contains("MSIE 10")) {  
         return request.getHeader("user-agent").replaceAll("MSIE [^;]*;", "MSIE 9.0;");  
       }  
       // IE 11: replace the whole agent-string into an MSIE 9.0 string for ADF 11.1.1.6 and below  
       // or MSIE 10.0 for ADF 11.1.1.7 or higher  
       if ("user-agent".equalsIgnoreCase(name) && request.getHeader("user-agent").contains("Trident")) {  
         //Choose your preferred version  
         String newAgentStr = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.2; Trident/6.0)";  
         return newAgentStr;  
       }  
       return request.getHeader(name);  
     }  
   }  
 }  
   

The filter has to be defined in the web.xml-file of your project. Make sure it is defined above the trinidad-filter, because this one is used by ADF to determine the user-agent.

web.xml
 ...  
  <filter>  
   <filter-name>IECompatibleFilter</filter-name>  
   <filter-class>view.IECompatibleFilter</filter-class>  
  </filter>  
  <filter>  
   <filter-name>trinidad</filter-name>  
   <filter-class>org.apache.myfaces.trinidad.webapp.TrinidadFilter</filter-class>  
  </filter>  
 ...  
  <filter-mapping>  
   <filter-name>IECompatibleFilter</filter-name>  
   <servlet-name>Faces Servlet</servlet-name>  
  </filter-mapping>  
  <filter-mapping>  
   <filter-name>trinidad</filter-name>  
   <servlet-name>Faces Servlet</servlet-name>  
   <dispatcher>FORWARD</dispatcher>  
   <dispatcher>REQUEST</dispatcher>  
  </filter-mapping>  
 ...  


Conclusion

The above solution is not the ideal situation, but as long as we are dealing with non-supported combinations of browsers and ADF versions, it might be a workaround. The nice things is that you don't have to change your application, but you only add the filter to the web.xml. Furthermore I think the solution is not limited to ADF applications.

I have tested this solution with ADF 11.1.1.6, 11.1.1.7 and 11.1.2.3, and they all seem to work under IE 11, but of course I cannot guarantee that it will work in all situations. Give it a try. 

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. 

woensdag 13 maart 2013

ADF BC: performance-issues when using View Accessors and List of Values

One of the features of ADF BC view objects is the possibility to define a model-based list of values for an attribute. This comes with a lot of functionality and can be defined in an easy way in the view object. However it appears that the default behavior of the JDeveloper and ADF has a negative effect on the performance of your application.

Suppose we have two view objects in out model-project:
  • EmployeesView
  • DepartmentsView
The EmployeesView has an attribute DepartmentId for which we want to define a choice-list with all deparments. To create such a list we must perform the following steps:

First we need to define a view accessor to the DepartmentsView.

fig 1 - View accessor

Second we need to define a List of values for the field DepartmentId.

fig 2 - List of Values


To show the effect of this default LOV definition I've added some logging information in the executeQueryForCollection method of the view objects base-class.

Now suppose we create a simple employees.jspx view and we drag-and-drop the EmployeesView as a table from the datacontrol-pallet to the page. The page will look something like this

fig 3 - employees.jspx

The log-info that is generated by this simple screen is as follows:

fig 4 - logging

As you can see, the executeQueryForCollection is called once for the EmployeesView, as expected, but many times for the DepartmentsView. The query to determine the choice-list values for the field DepartmentId is executed for every row in the EmployeesView. This is unnecessary and can have a negative effect on the performance of your application.

The behavior is caused by a property of the view accessor. If we take a closer look to the Property Inspector in fig. 1, we see a property 'Row Level Bind Values' with a value 'true'. The purpose of this property is to indicate that there are bind variables defined in the lookup view object that can have a different value for each row in the base view object. In out situation that is not the case. A valid example would be a list of values for the attribute ManagerId, where the list of managers is limited to the department of the employee.

Now lets turn the value of the 'Row Level Bind Values' to 'false' and run the screen again. This generates the following log-information:

fig 5 - logging with Row Level Bind Values = false
Now the executeQueryForCollection is called only once for the lookup DepartmentView. Much better!

Setting the value of the property to empty will have the same effect, as long as there are now bind variables in the lookup view object at all. If there are bind variables, an empty value will have the same effect as  the value 'true'. This seems logical, but if you assign literal values to the bind variables instead of 'row level bindings', it is still undesired.

Conclusion
The default behavior of JDeveloper when creating a view accessor is that it will set the property 'Row Level Bind Values' to 'true'. As we have seen this can have a (very) negative effect on the number of queries executed, and the performance of your application. Leaving the property blank works in many cases, but not always. My advise is to set this value explicitly to 'false' if you have no bind variables that really depend on some attribute values in the row.
The problem is that property is not visible in the create/edit window for a view accessor. Furthermore JDeveloper has it's own ideas of what the value for this property should be. Sometimes when you change something in the view object, the value for the property is changed automatically. You should be aware of this.

To deal with this issue I've created a small extension for JDeveloper that defines an auditrule that checks for the correct settings of the 'Row Level Bind Values' property. It also provides an automatically fix for the settings. I will soon make this extension available.

Remarks: I'm working with JDeveloper 11.1.1.6. I don't know the behavior of JDeveloper 11.1.2.* for this issue.

donderdag 19 april 2012

ADF 11g: Remove the LOV button from the keyboard tab-sequence: Part 2

In a previous post I described a way to remove the LOV button from the keyboard tab-sequence. This solution was based on a combination of a focus-clientListener and some Javascript. As soon as an LOV item gets focus, the Javascript searches the related LOV button and turns the tabIndex of the button to -1. This way, the button will not be in the tab-sequence anymore.
One comment on this solution was that it will not work for LOV items that are displayed as part of an <af:query> component. This is caused by the fact that an <af:query> component renders its items on the fly, so it is not possible to add a clientListener to an item. However, there is another solution for this problem.

Since the previous post, I've been working on functionality  to 100% keyboard enable our ADF application. This functionality is based on a Javascript library called jQuery, This library turns out to be a very powerful tool in combination with the ADF client side.In this post I will describe a way to remove the LOV button from the keyboard tab-sequence, using jQuery. The solution will cover all LOV items, including the ones in e.g. an <af:query>.

In general, what the solution does, is find all LOV buttons in the page (HTML-document), and turn their tabIndex into -1.For this we first need a Javascript to find and change all LOV buttons. And this is where jQuery comes into place:

Javascript
var lovIcons = $('a[id$="lovIconId"]');

This statements finds all HTML-elements of type <a> with an id that ends with 'lovIconId'. This results in a list of all the LOV buttons.The $ sign in the statement is a reference to the functions in the jQuery library.

The next step is to turn the tabIndex for all the items into -1:

Javascript
$(lovIcons).attr('tabIndex', -1);

In this post I will not go into the details of jQuery, but as you can see a few simple statements can do the job.

To make use of jQuery, download the library from this location: http://code.jquery.com/jquery-1.7.js. Make sure the library is available in your project, and include a reference to the library in your page:

    <af:document id="d1">
      <f:facet name="metaContainer">
        <af:group>
          <af:resource type="javascript" source="/jsLibs/jquery-1.7.js"/>
        </af:group>
      </f:facet>
   .....


Now we need a way to trigger this Javascript. ADF pages are dynamic documents that can show or hide LOV items during different requests. This means that it is not enough to run the script during document-load. The easiest way is to run the script during every request. Perhaps there are more fine-grained ways, but for this post I will keep it simple. To trigger the script we make use of a PhaseListener. This PhaseListener adds the script to every response:

PhaseListener
package view.beans;

import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

import oracle.adf.share.logging.ADFLogger;

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


public class DemoPhaseListener implements PhaseListener {
    private static final ADFLogger sLog = ADFLogger.createADFLogger(DemoPhaseListener.class);
    private static final long serialVersionUID = 1L;

    /**
     * placeholder method
     */
    @Override
    public void afterPhase(PhaseEvent phaseEvent) {
    }

    /**
     */
    @Override
    public void beforePhase(PhaseEvent phaseEvent) {
        if (phaseEvent.getPhaseId() == PhaseId.RENDER_RESPONSE) {

            FacesContext fctx = FacesContext.getCurrentInstance();
            ExtendedRenderKitService erks = Service.getRenderKitService(fctx, ExtendedRenderKitService.class);
            StringBuffer script = new StringBuffer();

            // Append the jQuery script to find all lov-icons and set their tabIndex to -1

            script.append("var lovIcons = $('a[id$=\"lovIconId\"]'); $(lovIcons).attr('tabIndex', -1);");
            erks.addScript(fctx, script.toString());
        }

    }

    @Override
    public PhaseId getPhaseId() {
        return PhaseId.RENDER_RESPONSE;
    }

}


The last step is to define the PhaseListener in the faces-config.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<faces-config version="1.2" xmlns="http://java.sun.com/xml/ns/javaee">
  <lifecycle>
    <phase-listener>view.beans.DemoPhaseListener</phase-listener>
  </lifecycle>
  <application>
    <default-render-kit-id>oracle.adf.rich</default-render-kit-id>
  </application>
</faces-config>


So now, if you run your application, all LOV-buttons are skipped! Soon I will write some more about he possibilities of jQuery in combination with ADF clientside scripts.

woensdag 21 maart 2012

ADF 11g: Remove the LOV button from the keyboard tab-sequence

The default behavior in ADF Faces when using the tab-key in a List of Values item is that the LOV icon behind the item will get the focus. For people who do a lot of data entry  this is not the preferred behavior, because they usually know the codes they have to enter. In combination with a function key defined to call the LOV dialog, there is no need to include the LOV icon in the tab-sequence. 

Looking for a way to solve this problem, I found several blogposts with a solution. Most of them are based on the usage of a clientListener of type 'keydown' on the LOV item. The clientListener calls a JavaScript function that checks the key pressed, and places the focus on the next input field when the tab-key is pressed. The challenge in these solutions is to determine what the next focus field should be. Finding the next item based on hardcoded id's isn't an elegant solution.


If we could influence the way the HTML was written by ADF Faces, the solution would be easy. The only thing we really need, is to set the HTML tabIndex attribute of the LOV icon to -1. This tells the browser to skip the icon from the tab sequence. However the af:inputListOfValues item does not support this feature declaratively. 

In the following I will explain how the tabIndex of the LOV icon can be set to -1. What we need is a clientListener and some JavaScript.


Javascript:
 function setLovTabIndex(event) {  
   var component = event.getSource();  
   var lovButtonId = component.getClientId() + '::lovIconId';  
   
   // We have to find the button using the document because finding it through Adf.Page.findComponent does not  
   // work. The icon is not a separate ADF component.  
   var lovButton = document.getElementById(lovButtonId);  
   
   // This is the trick: set tabindex to -1  
   lovButton.tabIndex = - 1;  
   
   // Remove the eventlistener to prevent action from being performed again. Not really a must, but cleaner.  
   // However this does not work in conjunction with the autosuggest feature on the lov-item.  
   // In that case the listener is not removed.  
   component.removeEventListener("focus", setLovTabIndex, false);  
   
   //event is handled on the client. Server does not need  
   //to be notified  
   event.cancel()  
 }  
   

This JavaScript should be included in a JavaScript library that is part of your application.

The next step is to add an  af:clientListener to the af:inputListOfValues item:
     <af:inputListOfValues id="employeesManagerId" ...>  
      <f:validator binding="#{bindings.EmployeesManagerId.validator}"/>  
      <af:convertNumber groupingUsed="false" pattern="#{bindings.EmployeesManagerId.format}"/>  
      <af:clientListener method="setLovTabIndex" type="focus"/>  
     </af:inputListOfValues>  
   

As you can see, the listener is fired when the LOV item gets the focus. The JavaScript will lookup the related LOV icon, and will set the tabIndex of the icon to -1. After that, the clientListener is removed. The advantage of this approach is that there is no need to have any knowledge about the 'next focus field'. Furthermore the code is only fired once when the LOV item gets focus.