Fixing Trinidad IE 11 Partial Page Request (PPR) Issue / Support All IE Browsers

Filed Under: JSF

Trinidad 1.2.14 is MyFaces implementation for JSF 1.2 specification. It’s mainly released by Apache MyFaces and it’s considered last official release that you can download from Apache MyFaces Trinidad site.

Actually, Trinidad library is a leading library that truly help you getting JSF application in the most easiest way, it’s contained a lot of important components that can help you avoid wasting your time creating or developing any component you may need.

This version of Trinidad is almost ideal but there’s one issue you may be suffering from when it comes to deal with, it’s actually doesn’t understand Ajaxifying request on IE 11 and that may cause a headache for a lot of developers whom want to use this feature.

This tutorial is a solid trial that would help you getting full-fledged Trinidad 1.2.14 library ajaxified comfortably on all version of IE including IE 11. You may review the internet and Trinidad JIRA but actually, you my be wondering that the Trinidad itself doesn’t provide any solution for that. All what you may find is just a scattered patch that’s required you to work on it to justify the needed solution.

Tools Used

I’ve assumed that you will use the below Tools/Envs for getting this practice applied:

  • Any JEE container.
  • JSF Trinidad 1.2.14 API & Impl.
  • JDK 1.6.
  • Eclipse Kepler 4.3.
  • Maven 3.2.1.

Ajax Concept In Trinidad

Ajax concept hasn’t vary differ from what already most of developers were knew. It’s simple partial submission for the page; instead of sending a full JSF Post-Back request into server, you’re just sending part of the page into server and update part of it in the response.

Here, you would see a true Ajax application that uses a Trinidad 1.2.14 upon Chrome browser. In fact and indeed the implementations of Ajax aren’t analogous to that degree that you may use different mechanisms for ِAjaxifying your application if you’re decided to use different JSF vendors. That is, there’s no standard way to Ajaxify, so it depends on the library that you’re going to use.

index.jsp


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core"%>
<%@ taglib prefix="h" uri="http://java.sun.com/jsf/html"%>
<%@ taglib prefix="tr" uri="http://myfaces.apache.org/trinidad"%>
<%@ taglib prefix="trh" uri="http://myfaces.apache.org/trinidad/html"%>

<f:view>
	<tr:form>
		<h:panelGrid columns="4">
			<h:outputLabel value="Enter your message here"></h:outputLabel>
			<tr:inputText value="#{messageBackingBean.message}" partialTriggers="ajaxAction"></tr:inputText>
			<tr:commandButton id="ajaxAction" text="Ajax Sending" action="#{messageBackingBean.addMessage}" partialSubmit="true"></tr:commandButton>
			<tr:commandButton id="nonAjaxAction" text="Non-Ajax Sending" action="#{messageBackingBean.addMessage}"></tr:commandButton>
		</h:panelGrid>
		<tr:statusIndicator inlineStyle="padding-left:400px;">
		  <f:facet name="busy">
		    <h:graphicImage url="/images/ajax-loader.gif"></h:graphicImage>
		  </f:facet>
		</tr:statusIndicator>
		<h:panelGrid columns="1">
			<f:facet name="header">
				<h:outputText value="Sent Messages"></h:outputText>
			</f:facet>
			<tr:table value="#{messageBackingBean.messages}" var="message" partialTriggers="ajaxAction">
				<tr:column>
					<h:outputText value="#{message}"></h:outputText>
				</tr:column>
			</tr:table>
		</h:panelGrid>
	</tr:form>
</f:view>

MessageBackingBean.java


package com.journaldev;

import java.util.ArrayList;

public class MessageBackingBean {

	private String message = "";
	private ArrayList<String> messages = new ArrayList<String>();

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

	public ArrayList<String> getMessages() {
		return messages;
	}

	public void setMessages(ArrayList<String> messages) {
		this.messages = messages;
	}

	@SuppressWarnings("static-access")
	public String addMessage() throws InterruptedException{
		// Wait for 5 seconds, just to ensure the action is invoked in an Ajax behavior
		Thread.currentThread().sleep(5000);
		this.messages.add(message);
		this.message = "";
		return "";
	}

}

faces-config.xml


<?xml version="1.0" encoding="UTF-8" ?>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"
	version="1.2">
	<application>
		<default-render-kit-id>org.apache.myfaces.trinidad.core</default-render-kit-id>
	</application>
	<managed-bean>
		<managed-bean-name>messageBackingBean</managed-bean-name>
		<managed-bean-class>com.journaldev.MessageBackingBean</managed-bean-class>
		<managed-bean-scope>session</managed-bean-scope>
	</managed-bean>
</faces-config>

pom.xml


<?xml version="1.0"?>
<project
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
	xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.journaldev</groupId>
	<artifactId>Trinidad-JSF-APP</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<name>Trinidad-JSF-APP Maven Webapp</name>
	<url>http://maven.apache.org</url>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>2.5</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>jsp-api</artifactId>
			<version>2.0</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax</groupId>
			<artifactId>jsf-api</artifactId>
			<version>1.2_13</version>
		</dependency>
		<dependency>
			<groupId>javax</groupId>
			<artifactId>jsf-impl</artifactId>
			<version>1.2_13</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
		<dependency>
			<groupId>org.apache.myfaces.trinidad</groupId>
			<artifactId>trinidad-api</artifactId>
			<version>1.2.14</version>
		</dependency>
		<dependency>
			<groupId>org.apache.myfaces.trinidad</groupId>
			<artifactId>trinidad-impl</artifactId>
			<version>1.2.14</version>
		</dependency>
	</dependencies>
	<build>
		<finalName>Trinidad-JSF-APP</finalName>
	</build>
</project>

web.xml


<?xml version="1.0" encoding="UTF-8"?>
<web-app
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	id="WebApp_ID" version="2.5" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
	<display-name>Trinidad-JSF-APP</display-name>
	<context-param>
		<param-name>javax.faces.CONFIG_FILES</param-name>
		<param-value>
					/WEB-INF/faces-config.xml
		</param-value>
	</context-param>
	<context-param>
		<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
		<param-value>server</param-value>
	</context-param>
	<servlet>
		<servlet-name>Faces Servlet</servlet-name>
		<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet>
		<servlet-name>resources</servlet-name>
		<servlet-class>org.apache.myfaces.trinidad.webapp.ResourceServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>Faces Servlet</servlet-name>
		<url-pattern>/faces/*</url-pattern>
	</servlet-mapping>
	<servlet-mapping>
		<servlet-name>resources</servlet-name>
		<url-pattern>/adf/*</url-pattern>
	</servlet-mapping>
</web-app>

Here’s detailed explanation for the code listed above:

  • Trinidad has initiated a PPR through using of its library that’s grabbed by resources Servlet. This script library will be injected into your JSF page as soon as it’s rendered and it would have this name /CONTEXT/adf/jsLibs/Common1_2_14.jsif you’re explored your page header.
  • The action Ajax Sending will be used to ajaxify the calling of addMessage method/action.
  • The action Non-Ajax Sending will be used to initiate normal PostBack request.
  • As soon as the Ajax request is triggered, the Ajax loader image will be displayed by statusIndicator Trinidad Tag. This will give you a good indicator for being the request is initiated using Ajax manner.
  • At the MessageBackingBean, you will be waiting there in for 5 seconds, just to give you a chance simulating heavy processing.

Now, let’s see a simple demonstration for what already implemented till now using a non error prone Chrome browser.

Below figure shows you the initial view of the index.jsp that’s already considered as default page.

Trinidad Application - Initial View

Enter a message inside the box below and submit the Ajax Sending action. You will notice that the ajax indicator loading image will be displayed, just wait for 5 seconds before the message is sent.

Trinidad Application - Ajax Message SendingTrinidad Application - Ajax Message Sending - Message Added

The same behavior can be achieved through using a default Post Back JSF action and the same result would be occurred with one major difference is being the request is Post back.

Trinidad Application - Non Ajax Message SendingTrinidad Application - Non Ajax Message Sending - Message Added

What’s The Problem

As you’ve noticed above, Ajax Trinidad has functioned properly on the Chrome where no issues were noticed. But what if you’re going to IE 11, you will absolutely get Ajax failed due to Trinidad Implementation missing.

Let’s look at the behavior below that shows you the impact of browsing the same Application above on the IE 11 – Notice, this issue weren’t noticed at any version of IE and that what i will explore in the next coming lines.

IE 11 Ajax IssueIE 11 - Message Wasn't Sent

Here’s below a detailed explanation for the behavior seen above:

  • Once you’ve clicked on the Ajax action, the ajax loading image will be displayed but actually the behavior is little bit different where the IE won’t get updated the Table of Sent Messages as well as an error message will be thrown says Connection Failed.
  • If you’ve reviewed your Console or Logger, you’re likely get an exception like below.

Thrown Exception


SEVERE: Servlet.service() for servlet Faces Servlet threw exception
java.lang.IllegalArgumentException
	at org.apache.myfaces.trinidadinternal.renderkit.core.ppr.PPRResponseWriter.<init>(PPRResponseWriter.java:59)
	at org.apache.myfaces.trinidadinternal.renderkit.core.CoreRenderKit.createResponseWriter(CoreRenderKit.java:609)
	at org.apache.myfaces.trinidadinternal.renderkit.RenderKitDecorator.createResponseWriter(RenderKitDecorator.java:54)
	at com.sun.faces.application.ViewHandlerImpl.renderView(ViewHandlerImpl.java:189)
	at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:189)
	at org.apache.myfaces.trinidadinternal.application.ViewHandlerImpl.renderView(ViewHandlerImpl.java:193)
	at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:110)
	at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:100)
	at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:139)
	at javax.faces.webapp.FacesServlet.service(FacesServlet.java:266)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
	at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:861)
	at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:606)
	at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
	at java.lang.Thread.run(Thread.java:662)
  • Actually, the above listed exception wasn’t thrown on any browser except the IE 11, even if you’re used IE 7, 8, 9 or 10 this issue won’t be noticed.
  • This above exception is caused due to unavailability of correct Agent value. That is, RequestContextImplgetAgent will be asked to determine the type of the Agent that actually try to display the view. By its turn, the AgentFactoryImpl_populateMozillaAgentImpl method will be called because the Agent description starts with Mozilla keyword.
  • The _populateMozillaAgentImpl method will return wrong value that’s affected the normal procedure of your Partial Page Trigger (PPR) handling. User agent of IE 11 is Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko and the _populateMozillaAgentImpl method won’t be able to recognize the Agent Type which will be finally just like in the figure below:

Agent value for IE 11

  • But if you’re modifying the user agent string from your IE browser to be Internet Explorer 10 or less and you tried to initiate a new Partial Page Request, you will find a different agent string, that will be Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)and the agent type will be just like figure below:

Agent value for IE 10

  • The same correct value you will still be able to receive as long as you used an IE version less than 11. Look below for the value of IE 8.

Agent value for IE 8

As a conclusion, the main issue is relevant for _populateMozillaAgentImpl that should be fixed to contain a proper handling for IE 11.

Importing Trinidad 1.2.14 Source Code Impl Library into Eclipse Project

To know the main cause of this issue, it’s worthy to get Trinidad Impl debugged to know from where we can start the fix procedure. Upon that, let’s basically download Trinidad 1.2.14 and look inside to know what the cause of this behavior.

  • Download Trinidad impl 1.2.14 source code from Trinidad Apache official site.
  • Unzip trinidad-1.2.14-src-all.zip into your hard storage.
  • From your Eclipse Project Explorer, click new – import and choose Maven project.
  • Paste your Trinidad location just as you see below.

Import Trinidad

  • Now, you’re able of getting Trinidad Impl compiled and packaged through using of both Maven command & Eclipse. Figure below shows you the success cleaning and packaging for Trinidad Impl library.

Success Cleaning & Packaging of Trinidad Impl

  • Now, you can provide your Fix for the AgentFactoryImpl and provide your own Trinidad Impl library.

Fixing AgentFactoryImpl

According for the previous investigation and to fix the issue of IE 11, I’ve provided you the new implementation of _populateMozillaAgentImpl to be like below:

_populateMozillaAgentImpl


  /**
   * Returns an AgentEntry for the "Mozilla" family of browsers - which
   * most at least pretend to be.
   */
  private void _populateMozillaAgentImpl(String agent,AgentImpl agentObj)
  {
	  int ieTridentIndex = -1;
    int paren = agent.indexOf('(');
    agentObj.setType(Agent.TYPE_DESKTOP); //Is this default realli okay??? These days Mobile agents also use Mozilla/xx.xx

    // No section to qualify the agent;  assume Mozilla/Netscape
    if (paren == -1)
    {
      agentObj.setAgent(TrinidadAgent.AGENT_NETSCAPE);
      agentObj.setAgentVersion(_getVersion(agent, agent.indexOf('/')));
    }
    else
    {
      paren = paren + 1;

      boolean isJDevVE = agent.indexOf("JDeveloper", paren) > 0;
      boolean isJDevJSVE = agent.indexOf("JDeveloper JS", paren) > 0;

      if (agent.indexOf("Konqueror", paren) >= 0)
      {
        agentObj.setType(Agent.TYPE_DESKTOP);
        agentObj.setAgent(Agent.AGENT_KONQUEROR);
        agentObj.setAgentVersion(_getVersion(agent, agent.lastIndexOf('/')));
      }
// Added to get IE 11 resolved & to handle all IE browsers
    else if ((ieTridentIndex = agent.indexOf("Trident", paren)) > -1) 
        {
          agentObj.setAgent(Agent.AGENT_IE);
  
          // As of IE8, the Trident version is the most reliable method to find the 
          // maximum capabilities of IE.  The IE WebBrowser Control by default is in IE7 
          // compatability - MSIE 7.0;
          // As of IE11, the "MSIE" token no loger exists.  
          
          //Trident/4.0 -> IE8
          //Trident/5.0 -> IE9
          //Trident/6.0 -> IE10
          //Trident/7.0 -> IE11
          Double ieTridentVersion = Double.valueOf(_getVersion(agent, ieTridentIndex + "Trident/".length() - 1));
          agentObj.setAgentVersion(String.valueOf(ieTridentVersion + 4.0));    
        }      
      else if (agent.startsWith("compatible", paren))
      {
        int ieIndex = agent.indexOf("MSIE", paren);

        if (ieIndex < 0)
        {
          // check for Palm
          int palmIndex = agent.indexOf("Elaine", paren);

          if (palmIndex > 0)
          {
            agentObj.setType(Agent.TYPE_PDA);
            agentObj.setAgent(TrinidadAgent.AGENT_ELAINE);
            agentObj.setAgentVersion(_getVersion(agent, palmIndex));
            agentObj.setPlatform(Agent.PLATFORM_PALM);
          }
        }
        else
        {
          agentObj.setAgent(Agent.AGENT_IE);
          agentObj.setAgentVersion(_getVersion(agent, ieIndex + 4));
        }
      }
      else
      {
        agentObj.setAgent(TrinidadAgent.AGENT_NETSCAPE);
        agentObj.setAgentVersion(_getVersion(agent, agent.indexOf('/')));
      }

      // try to determine the OS, if unknown
      if (agentObj.getPlatformName() == null || agentObj.getPlatformName().equals(Agent.PLATFORM_UNKNOWN))
      {
        // Hack: treat the JDeveloper agent as Windows,
        // so that we assume IE 6.0 Windows capabilities
        if ((agent.indexOf("Win", paren) > 0) || isJDevVE)
        {
          agentObj.setPlatform(Agent.PLATFORM_WINDOWS);
        }
        else if (agent.indexOf("Mac", paren) > 0)
        {
          agentObj.setPlatform(Agent.PLATFORM_MACOS);
        }
        else if (agent.indexOf("Linux", paren) > 0)
        {
          agentObj.setPlatform(Agent.PLATFORM_LINUX);
        }
        else if (agent.indexOf("Sun", paren) > 0)
        {
          agentObj.setPlatform(Agent.PLATFORM_SOLARIS);
        }
      }

      if (isJDevVE)
      {
        agentObj.__addRequestCapability(TrinidadAgent.CAP_IS_JDEV_VE,
                                        Boolean.TRUE);
        if (isJDevJSVE)
        {
          agentObj.__addRequestCapability(TrinidadAgent.CAP_IS_JDEV_JAVASCRIPT_VE,
                                          Boolean.TRUE);
        }

      }
    }
  }

Here’s detailed explanation for the code listed above:

  • We added the code commented to handle the IE 11 & all types of IEs.
  • You can download the full AgentFactoryImpl right here AgentFactoryImpl.
  • Replace the downloaded file or re-implement the method and make your code compiled and package the updated Trinidad impl library just like you saw in the Maven figure above.
  • Install your latest Trinidad Impl library using mvn clean package install.

Demonstrate PPR Sample Using IE 11 After Fixing

This figure below shows you a proper handling for the PPR request against IE 11.

IE 11 Fixed - PPR Demonstration

IE 11 Fixed - PPR Demonstration - Sent New Messages

Resources

  • Trinidad JIRA

Summary

Trinidad is a leading implementation for JSF and it provides you a different kinds of components that actually have the capability of initiating requests Post back or partially. The main issue you may got when it comes to deal with Trinidad latest release 1.2.14 is handling partial request on IE 11.  This tutorial will help you getting this issue resolved, so contribute us by commenting below.

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages