Thursday, December 08, 2011

JSF 1.2 (myFaces) + RichFaces 3.3.x + IE 9 = troubles

Note: below description uses Eclipse Indigo, Tomcat 7.0.28, MyFaces 1.2.12, RichFaces 3.3.3 (Final).

Requirements:
  • working JSF 1.2 sample application from here
  • RichFaces 3.3.x added to the project
You will learn:
  • how to fix problems with RichFaces 3.3.x for IE 9
Let's assume that You develop JSF 1.2 web application. It does not matter if Your application uses .jsp pages or Facelets with .xhtml pages. I assume that .jsp pages are in use. Then You decided to add some Ajax capabilities, new components and so on. So You simply take RichFaces and add them to the project. This is simple thing:
a) download latest RichFaces 3.3.3 (final), take those jars: richfaces-api-3.3.3.Final.jar, richfaces-impl-3.3.3.Final.jar, richfaces-ui-3.3.3.Final.jar and place them in WEB-INF\lib folder of Your web application. 
b) modify your web.xml file by adding there:
 <filter>
  <display-name>RichFaces Filter</display-name>
  <filter-name>richfaces</filter-name>
  <filter-class>org.ajax4jsf.Filter</filter-class>
 </filter>

 <filter-mapping>
  <filter-name>richfaces</filter-name>
  <servlet-name>Faces Servlet</servlet-name>
  <dispatcher>REQUEST</dispatcher>
  <dispatcher>FORWARD</dispatcher>
  <dispatcher>INCLUDE</dispatcher>
 </filter-mapping>
c) add proper taglib directives to each Your jsp page (note that for .xhtml pages syntax is different):
<%@ taglib uri="http://richfaces.org/a4j" prefix="a4j"%>
<%@ taglib uri="http://richfaces.org/rich" prefix="rich"%>
Now You can use RichFaces components. Let's try to modify our sample JSF 1.2 application by creating new web project named "JSFRichFaces". Then let's add ajax button (<a4j:commanButton>) and expandable toggle panel (<rich:simpleTogglePanel>). All modifications are shown below:




Everything works OK under Firefox, Chrome, IE 7, IE 8 - unless You use Internet Explorer 9. You can observe two problems: with RichFaces ajax components and RichFaces components layout.

RichFaces 3.3.x does not fully support IE 9. RichFaces developers encourage everyone to migrate into RichFaces 4.x which are IE 9 compatible. The main problem is that RichFaces 4.x works only with JSF 2.x application, and this is not so easy to migrate If You have many clients where You already delivered Your application...

Problem 1: ajax components do not work (some JavaScript errors).


When You try to press ajax button named "Go (RichFaces) " under IE 9, You can observe following error (open IE Developer's Toolbar Java Script console before):





Solution: You have to force IE 9 to act as IE 8, where RichFaces 3.3.x works OK. In order to do it You have to define X-UA-Compatible header for each page. You can do it in two ways:
a) add a special <meta> element for each page or
b) write a special filter which adds this header to the HTTP response

In our simple case it is sufficient to add a <meta> into the page <head> section:

<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE8" />
Remember that this <meta> must be placed as first element in <head> section!

Problem 2: RichFaces components have broken layout (problems with CSS).


When You open our sample application under IE 9, toggle panel has no CSS styles set at all. Under Chrome or Firefox everything looks OK. Checking HTTP request/response headers shows that for IE 9 proper CSS for RichFaces component (CSS located in the .jar file) is not loaded and server returns error:





Console under Eclipse shows that exception is thrown:



Under Firefox everything looks OK:






Why it happens? Accroding to that article, there was a change in IE 9 for MIME type handling. IE 9 ignores CSS styles if they are not delivered with a text/css MIME type.

Why it works for previous IE 7 and IE 8? Because also HTTP Request Accept header sent by IE 9 is different than sent from IE 7 or 8. IE 7/8 sent text/css, */* as Accept, where IE 9 sends only text/css as Accept.
Knowing that it is time to look inside myFaces source, to find the code which produces mentioned exception. Everything is located in HtmlRendererUtils.java (located in myfaces-impl-1.2.11.jar) in the selectContentType() method. It works for IE 7/8 because */* sent in Accept by IE 7/8 is on supported content type list, where text/css is not recognized at all.

Solution: add text/css support, then recompile HtmlRendererUtils.java and replace it in myfaces-impl-1.2.11.jar (or build complete myfaces-impl-1.2.11.jar). In order to avoid this mumbo-jumbo with Maven to build whole .jar, just create simple Java Project, create a HtmlRendererUtils.java in certain package, change it and compile, then replace compiled class in myfaces-impl-1.2.11.jar ;-)





Or download recompiled myfaces-impl-1.2.11.jar from here.


That's all. IE 9 works again without need to add Your web page to its compatibility view list or something. And You have time to migrate to JSF 2.x and RichFaces 4.x before IE 10 comes to market...

9 comments:

Gabriel Suarez said...

Your article was the only one that actually worked for me. Thank you very much!

Kundan Singh said...

thanks for such a good tutorial

Rolf Künzli said...

Ory you could just a Filter to your Application..
See code here

package ch.bedag.gba.capweb.util;

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;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
* IE9 Filter. Helps proper rendering on Internet Explorer 9.
*/
public class IE9Filter implements Filter {

private final static Log LOG = LogFactory.getLog(IE9Filter.class);

@Override
public void init(FilterConfig filterConfig) throws ServletException {
// noop
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// Hier zwingen wir den IE9 in den Kombatibilitaetsmodus
((HttpServletResponse) response).setHeader("X-UA-Compatible", "IE=EmulateIE8");

// Der IE9 sendet fuer css neu nur noch "text/css" als Accept header.
// Dies hat zur Folge, dass die Klasse HtmlRenderUtils folgende
// Exception wirft.
// "ContentTypeList does not contain a supported content type: text/css"
// Um dies zu verhindern kann man entweder das Myfaces Patchen oder eben
// den Request korrigierren.
// Wir haben uns für die zweite Lösung enschieden und verändern den
// Accept header. Neu wird es "text/css, */*" anstatt nur "text/css"
String accept = ((HttpServletRequest) request).getHeader("Accept");
if ("text/css".equals(accept)) {
chain.doFilter(new IE9HttpServletRequestWrapper((HttpServletRequest) request), response);
} else {
chain.doFilter(request, response);
}
}

@Override
public void destroy() {
// noop
}

private class IE9HttpServletRequestWrapper extends HttpServletRequestWrapper {

public IE9HttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}

@Override
public String getHeader(String name) {
String header = super.getHeader(name);
if ("text/css".equalsIgnoreCase(header)) {
header = "text/css, */*";
}
return header;
}
}
}

Anonymous said...

Great Solution. Thanks!

Xia Crusoe said...

Thanks! It helps me a lot!!

Xia Crusoe said...

It helps me a lot! Thanks!

Lal Sah said...

My application having this issue used MyFaces 1.2.6 and RichFaces 3.3.1. This fix worked great on IE9 after upgrading to RichFaces 3.3.3.

Lal Sah said...

Hi Paweł Nieścioruk, Just wondering if the fixed jar file available in any online repository as an artifact.
Thanks!

LeMIc said...

Thanks, excellent walk-through :)