Navigate <rich:tabPanel /> with keyboard

<rich:tabPanel /> does not support any keyboard interactions out of the box. One way to add it is to include the following javascript code:

 

/**
 * WCAG requires keyboard navigation for tabs. This code turns all inactive tabs into links,
 * so the browser starts tabbing over them. 
 * 
 * Author: Val Blant
 */
jQuery(document).ready(function() {
	jQuery("td.rf-tab-hdr-inact").children('span').each(function() {
		var tabName = jQuery(this).html();
		jQuery(this).empty();
		jQuery(this).append("<a href='#' style='text-decoration:none; color: black;' onclick='return false;'>" + tabName + "</a>");
	});
});

Eclipse Open Type dialog hangs

Tags

I noticed that on recent versions of Eclipse and Ubuntu, Eclipse’s Open Type dialog (Ctrl-Shift-T) often freezes for an annoying period of time as soon as you start typing your class name.

Here is the related bug:
https://bugzilla.gnome.org/show_bug.cgi?id=575873

Workaround

A temporary solution I found is to make sure that gail is not used by simply renaming the file:

/usr/lib/x86_64-linux-gnu:
lrwxrwxrwx 1 root root 22 Feb 4 16:18 libgailutil-3.so.0 -> libgailutil-3.so.0.0.0
-rw-r--r-- 1 root root 35440 Apr 16 2012 libgailutil-3.so.0.0.0
-rw-r--r-- 1 root root 31304 Mar 26 2012 libgailutil.so.18.0.1.OFF

Triggering a “change” ajax event on <rich:calendar />.

Problem Statement

Let’s say that we want to trigger an ajax request when the user changes the value in a <rich:calendar />, but it has to work when the user types the date into the text field manually, as well as when the user uses the calendar to pick the date.

<rich:calendar /> defines two events just for this purpose:

  • “change”: Triggered when the date is changed from the calendar
  • “inputchange”: Triggered when the text in the text field is changed manually

But how do we combine them?

Solution

I already had my calendar inside a Composite JSF Component, so my solution uses the following approach.

YourWebApp/WebContent/resources/components/calendar.xhtml:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:c="http://java.sun.com/jsp/jstl/core"
	xmlns:rich="http://richfaces.org/rich"
	xmlns:a4j="http://richfaces.org/a4j"
	xmlns:composite="http://java.sun.com/jsf/composite">

<composite:interface>
	<composite:attribute name="value" required="true" />
	<composite:attribute name="timeZone" required="true" />
	<composite:attribute name="disabled" default="false" />
	<composite:attribute name="displayValueOnly" default="false" />
	<composite:attribute name="required" />

	<composite:clientBehavior name="date_change" event="change" targets="#{cc.id}"/>
	<composite:clientBehavior name="date_change" event="inputchange" targets="#{cc.id}"/>
</composite:interface>

<composite:implementation>

	<c:if test="#{cc.attrs.displayValueOnly == false}">
		<rich:calendar id="#{cc.id}" 
				inputSize="10" 
				required="#{cc.attrs.required}" 
				enableManualInput="true" 
				value="#{cc.attrs.value}" 
				disabled="#{cc.attrs.disabled}" 
				datePattern="yyyy-MM-dd"
				onchange="if (DirtyFieldHandler) DirtyFieldHandler.setDirty();">
			<f:convertDateTime pattern="yyyy-MM-dd" timeZone="#{cc.attrs.timeZone}" />
		</rich:calendar>
	</c:if>

	<c:if test="#{cc.attrs.displayValueOnly == true}">
		<h:inputText value="#{cc.attrs.value}" disabled="true" id="#{cc.id}InputDate" size="10">
			<f:convertDateTime pattern="yyyy-MM-dd" timeZone="#{cc.attrs.timeZone}" />
		</h:inputText>
	</c:if>

</composite:implementation>
</html>

Then we use the component like so:

  <comp:calendar value="#{backingBean.approvedDate}">
     <a4j:ajax event="date_change" execute="@this" />
  </comp:calendar>

“Error: During update” from <rich:dataTable /> on ajax footer refresh

Problem Statement

Consider the following test code:

<rich:dataTable id="testTable" 
	value="#{dataTableTestBackingBean.rowsModel}"
	var="rowVar">
	
	<f:facet name="header">
	   <rich:column>
		<h:outputText value="Column 1" />
   	   </rich:column>
	</f:facet>
	
	<rich:column>
	   <h:inputText id="valTest" value="#{rowVar.col1}" >
       	 	<a4j:ajax 
       	 		event="blur" 
       	 		render="testColumn, footerTest" 
       	 		limitRender="true" 
       	 		execute="@this" />
		</h:inputText>
	</rich:column>
	<rich:column>
	   <h:outputText id="testColumn" value="#{dataTableTestBackingBean.footerValue}" />
	</rich:column>




	<f:facet name="footer">
	   <rich:column>
		<h:outputText id="footerTest" value="#{dataTableTestBackingBean.footerValue}" />
	   </rich:column>
	</f:facet>
</rich:dataTable>

This example will fail with the following Javascript error in the browser:

Error: During update: formId:testTable:0:footerTest not found

This is expected, b/c the correct id for the footer is actually formId:testTable:footerTest.

The bug is in org.richfaces.context.ComponentIdResolver. Here is what happens:

  1. The RenderComponentCallback is executed on the input component (‘valTest’ in the example) and it reads the list of short ids to render from the attached Ajax Behavior. Note that we got here by walking the parent data table model, so the current rowKey is 0.
  2. RenderComponentCallback asks ComponentIdResolver to resolve short ids into client IDs
  3. ComponentIdResolver starts ascending up the tree from ‘valTest’ and looking at facets and children until it finds ‘footerTest’.
  4. At this point it asks for the clientId of ‘footerTest’ w/o regard for the fact that the data model has a rowKey that is set 0

So, we get the wrong id b/c we call UiData.getClientId() outside of the normal walking order of that model.

I have logged a bug here: https://issues.jboss.org/browse/RF-11134

Workaround

I noticed that if I nest the above data table in another data iterator, then the footer update suddenly starts working. I investigated and it turned out that nested table will have getClientId() called on all of its components by the parent table, and since clientIds are cached, by the time ComponentIdResolver gets to it, it will use the cached id, rather than looking it up at the wrong time. Non-nested tables simply don’t get around to calling getClientId() on their components before ComponentIdResolver runs, which is probably the real problem.

Client-side Fix

Until the problem is fixed in RichFaces, the following client-side code fixes the problem. The idea here is to intercept JSF Response handler calls and check if all IDs in the responseXML are in fact present in the page. If they are not, use the simple ID to find the real clientId and correct it in the response. After that is done, control is returned to the usual RF Ajax handling code.

YourWebApp/WebContent/resources/javascript/richfaces-ajax-fix.js:

/**
 * Temporary fix for https://issues.jboss.org/browse/RF-11134
 * 
 * Once the ComponentIdResolver or UIDataAdapter.saveChildState is fixed,
 * this file will no longer be needed.
 * 
 * @author Val Blant
 */

if (!window.RichFacesAjaxFix) {
	window.RichFacesAjaxFix = {};
}


if ( typeof jsf != 'undefined' ) {

(function($, raf, jsf) {
	
	raf.ajaxContainer = raf.ajaxContainer || {};
	
	if (raf.ajaxContainer.jsfResponse) {
		return;
	}

	/** 
	 * Save the original JSF 2.0 (or Richfaces) method 
	 */
	raf.ajaxContainer.jsfResponse = jsf.ajax.response;

	/**
	 * Check if all IDs in the responseXML are in fact present in the page. 
	 * If they are not, use the simple ID to find the real clientId.
	 */
	jsf.ajax.response = function(request, context) {
		
		var changes = raf.ajaxContainer.extractChanges(request);
		
		for (var i = 0; i < changes.length; i++) {
			var change = changes[i];

			var oldClientId = raf.ajaxContainer.getClientId(change);
			if ( oldClientId == null ) {
				// This is probably not an "update" change.
				continue;
			}
			var shortId = raf.ajaxContainer.getShortId(change);
			

            // Check if this target element actually exists. If not, then we found the bug
			//
            var target = document.getElementById(oldClientId);
            if ( !target ) {
            	
	            // Find the target by shortId
            	targets = jQuery("[id$='" + shortId + "']");
            	
            	if ( targets.length === 0 ) {
            		// ok.... this target is not on the page at all. Eject.
            		continue;
            	}
            	
            	if ( targets.length === 1 ) {
            		// Only one target found, so we can derive the exact clientId easily
            		var newClientId = targets.first().attr("id");
            	}
            	else {
            		// This shouldn't happen, b/c nested data tables do not have this bug
            		//
            		// However.... looks like this can happen if we find an element with the same
            		// short id on another tab.
            		// We pick the right one with a VERY BADLY CODED approach - we choose the target with 
            		// the id whose length is closest to the original clientId length, b/c they should differ in just
            		// the row index. It's unreliable, but since this is a temporary solution, I'm not spending time to
            		// code this properly.
            		//
            		var smallest_length_delta = 1000;
            		var chosenidx = -1;
            		for (var k = 0; k < targets.length; k++) {
            			var length_delta = oldClientId.length - targets.get(k).id.length ;
            			if ( length_delta < smallest_length_delta ) {
            				chosenidx = k;
            				smallest_length_delta = length_delta;
            			}
            		}
            		
            		var newClientId = targets.get(chosenidx).id;
            	}
            	
            	// Now we have the clientId, so we just need to replace it
            	// in the ID attribute and in the markup itself
            	//
            	if ( newClientId ) {
            		raf.ajaxContainer.fixChange(change, oldClientId, newClientId);
            	}
            }
		}

		// Delegate to the old code
		//
		raf.ajaxContainer.jsfResponse(request, context);

	};
	
	/**
	 * Returns the list of elements under the 'changes' element in the AJAX response
	 */
	raf.ajaxContainer.extractChanges = function(request) {
		var changes = [];
		var xml = request.responseXML;
		
		if (xml !== null) {
			var pr = xml.getElementsByTagName("partial-response");
			
			if ( pr && pr.length > 0 ) {
				var responseType = pr[0].firstChild;
				
				if (responseType && responseType.nodeName === "changes") {
					changes = responseType.childNodes;
				}
			}
		}
		
		return changes;
	};
	
	raf.ajaxContainer.getClientId = function(change) {
		return change.getAttribute('id');
	};

	/**
	 * Return the id part after the last ':' 
	 */
	raf.ajaxContainer.getShortId = function(change) {
		var clientId = raf.ajaxContainer.getClientId(change);
		var ncs = clientId.split(":");
		var shortId = ncs[ncs.length - 1];
		
		return shortId;
	};

	/**
	 * Update the id of the given change element and the content with the newClientId
	 */
	raf.ajaxContainer.fixChange = function(change, oldClientId, newClientId) {
    	change.setAttribute("id", newClientId);
    	
    	for (var i = 0; i < change.childNodes.length; i++) {
            node = change.childNodes[i];
            node.nodeValue = node.nodeValue.replace(oldClientId, newClientId);
        }
	};


}(jQuery, window.RichFacesAjaxFix, jsf));
}

Dirty <rich:tabPanel />

Tags

,

I have recently had to implement a new requirement for all pages in our app that have a RichFaces Tab Panel. This post is about RichFaces 4.2.Final.

Requirement

Change tabs with Unsaved Changes:
1. Issue the following warning:

There are pending changes to the current tab that have not been saved. Do you wish to proceed with the tab change, or cancel and return to the tab?

2. The User may elect to either:
2.1. Proceed; The changes to the current tab are discarded.
2.2. Cancel; The use case continues at the step at which the user elected to change tabs.

Analysis

rich:tabPanel provides us with the following tools:

  • We can call a server method when the tab is changed (if we use ajax), but w/o ability to stop the tab change or show any warnings
  • We can call arbitrary javascript with ability to warn the user and abort tab switch, but the javascript is rendered at page render time, not at tab switch time, so no info from the server can be communicated

I did not want to get into server side tracking of all changes, since that would really complicate all pages, so the solution must be client side only.

Solution

I have created a very simple framework that I call Dirty Field Handler. It works as follows:

  • dirtyFieldHandler.js attaches a “delegated” event handler to the document, which will handle all “change” events from any input field on the page
  • Developer can use the DirtyFieldHandler framework as follows:
<!-- Include the framework on the page that needs it -->
 	<h:outputScript library="javascript" name="dirtyFieldHandler.js" target="head"/>

 	.....

 	<rich:tabPanel
 		switchType="ajax" <!-- Can't call the server w/o using ajax tab switches! -->
 		onbeforeitemchange='return DirtyFieldHandler.warnUser("#{msgs["common.warn.unsavedTab"]}");' <!-- Smart Javascript popup -->
 		itemChangeListener="#{initiativeBackingBean.tabSwitchAction}"> <!-- Notify the server that tab was switched -->

 		......

	</rich:tabPanel>

Code

 
dirtyFieldHandler.js:

/**
 * Provides the ability to detect if any input fields on the page were dirtied.
 * 
 * Exposes DirtyFieldHandler.warnUser(message, [true/false]) function that can be called
 * by the page to warn the user about dirty fields.
 * 
 * @author Val Blant
 */

if (!window.DirtyFieldHandler) {
	window.DirtyFieldHandler = {};
}


(function($, dfh) {
	
	dfh.setDirty = function() {
		dfh.dirty = true;
	}

	dfh.resetDirty = function() {
		dfh.dirty = false;
	}

	/**
	 * Warns the user if any fields are dirty. If the user chooses
	 * to navigate away, clears the dirty flag, unless the 2nd parameter is used
	 * to override that behavior
	 */
	dfh.warnUser = function(event, message, clearDirty) {
		var leavePage = true;
		clearDirty = typeof clearDirty !== 'undefined' ? clearDirty : true;
		
		if (dfh.dirty) {
			leavePage = confirm(message);
			if ( leavePage && clearDirty) {
				dfh.resetDirty();
			}
		}

		return leavePage;
	}
	
	
	
	// Init dirty flag
	//
	dfh.resetDirty();
	
	// Attach a "delegated" event handler to the document. All events that bubble
	// from input elements under this document will execute the handler.
	// We use a delegated handler b/c this approach will detect input field
	// changed or added by ajax requests
	//
	$(document).on("change", ":input", dfh.setDirty)

}(jQuery, window.DirtyFieldHandler));

Taming Weblogic

Tags

,

When things go bad in Weblogic, finding the problem can be extremely tricky. This post outlines some troubleshooting techniques that can save you some serious amounts of time and frustration.

Introduction

In order to deploy an application on WebLogic (WLS from here on), you must first create a domain. You can do this by running:

val@thinkpad:/opt/Oracle/Middleware/wlserver_10.3/common/bin$ ./config.sh

After that you need to create a “Machine” (Environment -> Machines) and point it to a running instance of a Node Manager. Then you create a Server (Environment -> Servers) and point it to the Machine. Then you can specify Deployments (EARs, WARS, etc) and run them on a specific server.

Deployments can either be archive files, or an exploded version of an archive on the file system. This is especially useful if you are tweaking your deployment by making modifications to it from Eclipse.

For example, let’s say you wanted to make some changes and redeploy the EAR. The steps would be:

  1. Stop your Server: Environments -> Servers -> Example_Server -> Control -> Shutdown
  2. Make the changes in Eclipse
  3. Export your ear or weblogic jar. The structure of a Weblogic application is explained in confusing detail here: http://docs.oracle.com/cd/E13222_01/wls/docs81/programming/environment.html
  4. Go to http://localhost:7001/console/ -> Deployments -> Update. This step assumes that you have previously deployed the app.
  5. Start the server

Starting Weblogic

val@thinkpad:/opt/Oracle/Middleware/wlserver_10.3/server/bin$ ./setWLSEnv.sh
val@thinkpad:/opt/Oracle/Middleware/wlserver_10.3/server/bin$ ./startNodeManager.sh
val@thinkpad:/opt/Oracle/Middleware/user_projects/domains/base_domain$ ./startWebLogic.sh

Changing Java interpreter

/opt/Oracle/Middleware/wlserver_10.3/common/bin/commEnv.sh:

@@ -129,7 +129,7 @@ if [ -z "${JAVA_HOME}" -o -z "${JAVA_VENDOR}" ]; then
   JAVA_HOME="/opt/Oracle/Middleware/jrockit_160_14_R27.6.5-32"
   # Set up JAVA VENDOR, possible values are
   #Oracle, HP, IBM, Sun ...
-  JAVA_VENDOR=Oracle
+  JAVA_VENDOR=Sun
   # PRODUCTION_MODE, default to the development mode
   PRODUCTION_MODE=""
 fi

Tweaking VM memory arguments

Go to /opt/Oracle/Middleware/user_projects/domains/base_domain/bin/setDomainEnv.sh and find the section for the chosen jdk and architecture. PermGen options are in the same file.

Logs

The logs you will be interested in are located under the server directory. For example:

/opt/Oracle/Middleware/user_projects/domains/base_domain/servers/Example_Server/logs

There will be .log files, which are less verbose and .out files, which is what you’ll want most of the time.

By default WLS is configured to provide as little information as possible in those logs, which is why it is unlikely that you’ll be able to fix your problem with default configuration. However, it is possible to make those logs much more verbose.

Debug Logging

-Dweblogic.StdoutDebugEnabled=true 
-Dweblogic.log.RedirectStdoutToServerLogEnabled=true

Classloader Logging

If you are dealing with NoClassDefFoundError, or other classloading issues, turn on classloader logging:

-Dweblogic.utils.classloaders.ClasspathClassFinder=true 
-Dweblogic.utils.classloaders.GenericClassLoader.Verbose=true 
-Dweblogic.utils.classloaders.ChangeAwareClassLoader.Verbose=true

General Debug Flags

-Dlog4j.debug=true
-Dweblogic.debug.DebugConfigurationEdit=true
-Dweblogic.debug.DebugDeploymentTaskRuntime=true
-Dweblogic.debug.DebugDeploymentManagerAdmin=true
-Dweblogic.debug.DebugDeploymentManagerTarget=true
-Dweblogic.debug.DebugDeploymentOperationsAdmin=true
-Dweblogic.debug.DebugDeploymentManagerTargetOperations=true
-Dweblogic.debug.DebugDeploymentServiceApiTargetCalls=true
-Dweblogic.debug.DebugDeploymentServiceApiAdminCalls=true
-Dweblogic.debug.DebugDeploymentServiceApiAdminCallback=true
-Dweblogic.debug.DebugDeploymentServiceApiTargetCallback=true
-Dweblogic.debug.DebugDeploymentServiceStatusUpdatesAdmin=true
-Dweblogic.debug.DebugDeploymentServiceTransport=true
-Dweblogic.debug.DebugDeploymentServiceStatusUpdatesTarget=true

Even more debug flags

http://middlewaremagic.com/weblogic/?page_id=1096

Configuring Logging

These settings need to be passed in as VM arguments to the server JVM. This is done here:

Environment -> Servers -> Example_Server -> Server Start -> Arguments

For example, my Arguments field looks like this right now:

-Dweblogic.StdoutDebugEnabled=true -Dweblogic.log.RedirectStdoutToServerLogEnabled=true -Xms512m -Xmx1024m -XX:MaxPermSize=512m

Debugging with Eclipse

If increased logging did not help you, it’s time to get serious :). We’ll connect to the running WLS JVM with the Eclipse Debugger and debug the internals of WLS in order to find the problem.

  • Make sure that JadClipse plugin is installed and working properly.
  • Add the following parameters to Server -> Server Start -> Arguments:
-Xdebug -Xrunjdwp:transport=dt_socket,address=1044,server=y,suspend=y

suspend=y means that when you start the server JVM, it will immediately get suspended and wait for a debugger to attach.

You might also want to add other VM arguments during this process. For example, my Arguments field looks like this:

-Xdebug -Xrunjdwp:transport=dt_socket,address=1044,server=y,suspend=y -Dweblogic.utils.classloaders.ClasspathClassFinder=true -Dweblogic.utils.classloaders.GenericClassLoader.Verbose=true -Dweblogic.utils.classloaders.ChangeAwareClassLoader.Verbose=true -Dweblogic.StdoutDebugEnabled=true -Dweblogic.log.RedirectStdoutToServerLogEnabled=true -Xms512m -Xmx1024m -XX:MaxPermSize=512m
  • Go to Eclipse, open your project’s Build Path and include /opt/Oracle/Middleware/wlserver_10.3/server/lib/weblogic.jar.
  • Create a Remote Java Application debug launcher. Address is localhost:1044
  • Start the server from the WLS Admin console. The status of the server will change to STARTING and nothing else will happen, b/c the server process is now suspended.
  • Run the Remote Java Application debug launcher you created earlier. The WLS server will continue booting and you should now be able to place breakpoints into WebLogic code!

That’s it – happy debugging!

Primefaces and Broken Head Resource Ordering

Tags

, ,

I have recently tried using PrimeFaces with a RichFaces application. I am very impressed with PrimeFaces. It is a well designed, simple, yet powerful JSF framework with a lot of cool components. Unfortunately, it does have bugs that I found as soon as I tried to use it.

Introduction

PrimeFaces is an open source JSF component suite with various extensions.

  • Rich set of components (HtmlEditor, Dialog, AutoComplete, Charts and many more).
  • Built-in Ajax based on standard JSF 2.0 Ajax APIs.
  • Lightweight, one jar, zero-configuration and no required dependencies.
  • Ajax Push support via websockets.
  • Mobile UI kit to create mobile web applications for handheld devices.
  • Skinning Framework with 35+ built-in themes and support for visual theme designer tool.
  • Extensive, clear documentation.
  • Large, vibrant and active user community.

Interaction with JSF stack

PrimeFaces uses the following classes to insert itself into the JSF framework. i.e – these are the classes which will be doing stuff to our requests, even if we are not using any PrimeFaces components!

  • org.primefaces.context.PrimePartialResponseWriter (affects AJAX requests)
  • org.primefaces.context.PrimePartialViewContext (affects AJAX requests)
  • org.primefaces.application.PrimeResourceHandler (affects resource handling)

Head Resource Ordering Bug

PrimeFaces comes with some very cool features that allow you to control where your resources are going to be rendered in the <HEAD/>. You can use facets to place your resources into “first”, “middle” and “last” positions. This is a nice feature, since with RichFaces I had to place our style and script tags at the end of the <BODY/>, to make sure that they are rendered after the RF resources. This order is important, since it allows us to override RF styles and scripts.

However, there is a very obvious bug in org.primefaces.renderkit.HeadRenderer which makes this feature quite destructive to our app. Simply including the PrimeFaces jar causes all of our script and style overrides to stop working, b/c the app resources are placed before all others.

There are other issues with this feature as well: http://code.google.com/p/primefaces/issues/detail?id=4807

Workaround

The problem is explained here: http://code.google.com/p/primefaces/issues/detail?id=4807#c1. Even though I know how to fix the problem, I do not wish to start maintaining our own PrimeFaces patches at this point of low adoption. So, the workaround is to disable Head Resource Ordering entirely for now:

WEB-INF/faces-config.xml:

<renderer>
   <component-family>javax.faces.Output</component-family>
   <renderer-type>javax.faces.Head</renderer-type>
   <renderer-class>com.sun.faces.renderkit.html_basic.HeadRenderer</renderer-class>
</renderer>

This means that we now have to manually hardcode the theme include for PrimeFaces components:

WEB-INF/layout/mainTemplate.xhtml:

<h:outputStylesheet library="primefaces-bluesky" name="theme.css" target="head" />