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. 

22 opmerkingen:

  1. Thanks! Now I can fix my problem with our old version of Apache MyFaces Trinidad.

    If you are running IE10 in Compatibility View , i think you get
    Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.2; Win64; x64; Trident/6.0; .NET4.0E; .NET4.0C)

    BeantwoordenVerwijderen
  2. I am working on 11.1.1.5 and IE 11.Do i need to change any of code above?Please let me know where do i need to make any changes.

    BeantwoordenVerwijderen
    Reacties
    1. hi,

      I am using webcenter 11.1.1.5 and IE 11 . But the same compatibility error is coming. Any thing else needs to be done apart from the above code and web.xml entry?

      Verwijderen
  3. Hi, as far as I know ADF 11.1.1.5 is certified against IE 9.0, so you don't have to change anything. If you have to simulate a lower version of IE change the 'MSIE 9.0' part in the return string into the proper version.

    Regards, Jonas

    BeantwoordenVerwijderen
  4. ok.Why there is code difference in filter mentioned in above post and in sample you provided for zip.In the zip demo you don't have getHeader method.Why not.?

    BeantwoordenVerwijderen
    Reacties
    1. oh i got that.there is no difference.ignore my previous comment

      Verwijderen
  5. Nice job! Fantastic solution working fine for mi
    Abel Manzanera
    UAB

    BeantwoordenVerwijderen
  6. But it doesn'w work with 11.1.2.1 version and/or with jsf pages. Do you have any idea how to achieve same thing for jdev11R2?
    Thanx,
    Patrik

    BeantwoordenVerwijderen
    Reacties
    1. Sorry, I have no experience with version 11.1.2.1 so I don't know why this is not working.

      Verwijderen
  7. Hi Jonas,

    Thanks for your job but it seems doesn't work with a Jdev 12.1.2 application. Any suggestion?

    Best regards,
    Jose.

    BeantwoordenVerwijderen
  8. Thank you, it's a lifesaver solution. Working for webcenter 11.1.1.8.

    Regards,
    Koray

    BeantwoordenVerwijderen
  9. Hi, i have made changed as per your way .. but still i'm getting browser compatibility Error message
    i'm developing Webcenter portal with JDEV 11.1.1.17

    BeantwoordenVerwijderen
  10. Hi! added this servlet to my application. I'm having IE 8.0. So, can you tell me what necessary changes we need to make in the code.

    BeantwoordenVerwijderen
    Reacties
    1. Sachin, when you are using IE 8.0, this feature will not work. It is mentioned to 'downgrade' IE 10.0 and higher to IE 9.0 so it is compatible with JDEV 11.1.1.6/7. IE 8.0 should work properly.

      Verwijderen
  11. Be sure to define IECompatibleFilter before the ADF filters to make it work

    BeantwoordenVerwijderen
  12. hi,

    I am using webcenter 11.1.1.5 and IE 11 . But the same compatibility error is coming. Any thing else needs to be done apart from the above code and web.xml entry?

    BeantwoordenVerwijderen
  13. Deepak,

    I'm not using webcenter so I'm not sure why this doesn't work. As far as I know ADF 11.1.1.5 is certified against IE 9.0, so you don't have to change anything. If you have to simulate a lower version of IE change the 'MSIE 9.0' part in the return string into the proper version. Could you check the generated meta-tag that ADF generates in the response after the adjustments you made?

    Regards, Jonas

    BeantwoordenVerwijderen
  14. Useful post ! Thank you !

    I have seen it worked for WebCenter Portal PS5 (11.1.1.6.0) in non-clustered environment.
    But in clustered environment, even though the header changed, but something was reverting it back.

    BeantwoordenVerwijderen
  15. HI Jonas,

    I am using Oracle ADF 11.1.1.4 version and IECompatibilityFilter is called only after trinidad. I am shown the browser not supported error before the page is loaded.
    I have copied the filter mapping as below. Please advise if there are any workaround here



    IECompatibleFilter
    /*
    Faces Servlet
    FORWARD
    REQUEST


    JpsFilter
    Faces Servlet
    FORWARD
    REQUEST
    INCLUDE


    trinidad
    Faces Servlet
    FORWARD
    REQUEST

    BeantwoordenVerwijderen