Posts Tagged ‘javascript

30
Apr
11

JavaScript Sleep

This page describes a simple implementation of a sleep function in javascript.

Java has a Thread.sleep() functionality however there is nothing like this in javascript. In order to obtain this functionality you will need to create a custom function.

This is a simple way to implement a sleep function in JavaScript. It works by implementing an semi-infinite loop. In each iteration it checks and breaks out, if the difference in the start time and the current time is greater than the number of milliseconds specified as the argument.

function sleep(milliseconds) {
  var start = new Date().getTime();
  for (var i = 0; i < 1e7; i++) {
    if ((new Date().getTime() - start) > milliseconds){
      break;
    }
  }
}

Call the function like this:

sleep(1000); // sleep for 1 second

This is a form of busy waiting… But currently the only way. Please post a comment if you disagree.

27
Mar
11

Preventing XSS using commons validator

This page describes the process of validating html submitted form to prevent a Cross Site Scripting attack using commons validator.

Background

Adding input validation to your application adds an extra layer of security however its only the second best method to protect against cross site scripting attacks.[1]

The best method is to is to encode the output before displaying it to the user.[2]

Regardless, adding more layers of security doesn’t hurt.

Approach

The approach taken here is to white-list characters that are allowed to be entered by creating a custom validator. Developers can use “mask” rule however this

  1. clutters up the validator.xml file
  2. could possibly interfere with a a field’s existing mask rule

Requirements

  1. Successful completion of my previous post

Procedure

We will be re-using the project created in my previous post. Or you can skip the implementation and just follow along below.

Create a Custom Validator

The following class extends the FieldChecks class provided by the struts framework. The extra method is based on the mask method of the parent class. The only difference is that the mask pattern is hard coded into the implementation of the method.

src/main/java/com/test/InfoSecurityChecks.java

package com.test;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.validator.Field;
import org.apache.commons.validator.GenericValidator;
import org.apache.commons.validator.ValidatorAction;
import org.apache.commons.validator.ValidatorUtil;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.validator.FieldChecks;
import org.apache.struts.validator.Resources;

public class InfoSecurityChecks extends FieldChecks {
	private static final long serialVersionUID = 1L;
	private static final Log log = LogFactory.getLog(InfoSecurityChecks.class);

	public InfoSecurityChecks() {
		super();
	}

	/**
	 * The code for this method was taken from the mask method of the parent
	 * class. The mask regular expression has been hard coded to only allow
	 * characters that are approved.
	 *
	 */
	public static boolean validateInfoSec(Object bean, ValidatorAction va,
			Field field, ActionErrors errors, HttpServletRequest request) {
		System.out.println("infosec validator ran");
		// The following is allowed
		// alpha numeric characters and space
		// special characters except < > ( )
		String mask = "^[A-Za-z0-9!\"\\\\#$%&'*+,/:;=\\.?@_\\`{\\|}\\~\\-\\^ ]*$";
		String value = null;
		if (isString(bean))
			value = (String) bean;
		else {
			value = ValidatorUtil.getValueAsString(bean, field.getProperty());
		}
		System.out.println(value);
		try {
			if ((!(GenericValidator.isBlankOrNull(value)))
					&& (!(GenericValidator.matchRegexp(value, mask)))) {
				errors.add(field.getKey(), Resources.getActionError(request,
						va, field));
				System.out.println("did not match returning false.");
				return false;
			}
			return true;
		} catch (Exception e) {
			 log.error(e.getMessage(), e);
		}
		return true;
	}
}

Modify the Validator Rules

Modify the validator-rules.xml and add an additional “validator” section towards the end.

src/main/webapp/WEB-INF/validator-rules.xml

      <validator name="infosec"
            classname="com.test.InfoSecurityChecks"
               method="validateInfoSec"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.apache.struts.action.ActionErrors,
                       javax.servlet.http.HttpServletRequest"
                  msg="errors.infosec">
      </validator>

Add the following new line to the application.properties

src/main/resources/application.properties

errors.infosec=The input contains invalid characters.

Modify the validation.xml and add “infosec” to the end of the depends attribute of the “lastName” attribute of the “userInformation” form.

src/main/webapp/WEB-INF/validation.xml

    <field property="lastName" depends="required,infosec">
        <arg key="userInformation.lastName" />
    </field>

Test the application

Return to the project top level directory and execute the following command to start the jetty servlet engine.

mvn clean compile jetty:run

Navigate to the following URL:

http://localhost:8080/

An extJS form should display allowing you to enter values. Click submit and an Ajax form submission will be made to the Struts Action. Any validation failures will show next to the component.

Try entering a <script>alert(‘test xss’);</script> in the last name field.

System should display a validation failure message indicating that there are invalid characters in the input.

References

[1] XSS (Cross Site Scripting) Prevention Cheat Sheet
[2]
http://www.ibm.com/developerworks/web/library/wa-secxss/

13
Mar
11

Ajax Simulated Combo Boxes using ExtJS

This page describes the process of creating a combo box that behaves like its Ajax driven. Adding this component is easy and with very little effort you can improve the usability of your page.

Requirements

  • Text Editor
  • HTML Web Server

Procedure

  1. Include the proper script statements to reference the ext js libraries. (see below)
  2. Create an “id” field for the combo box
  3. Insert the highlighted code below onto your page and specify the “id” of the component you want to enhance in the “transform” field of the js code below.

index.html

<html>
<head>

<link rel="stylesheet" type="text/css"
    href="http://dev.sencha.com/deploy/ext-3.3.1/resources/css/ext-all.css"/>

<script type="text/javascript"
    src="http://dev.sencha.com/deploy/ext-3.3.1/adapter/ext/ext-base-debug.js">
</script>
<script type="text/javascript"
    src="http://dev.sencha.com/deploy/ext-3.3.1/ext-all-debug.js">
</script>

<script type="text/javascript">

function buildWindow() {
 
var converted = new Ext.form.ComboBox({
    typeAhead: true,
    triggerAction: 'all',
    transform:'state',
    width:135,
    forceSelection:true
});

}
Ext.onReady(buildWindow);
</script>
 
</head>
<body>

<div>
Transformed select:<br/> <select name="state" id="state">

<option value="AL">Alabama</option>
<option value="AK">Alaska</option>
<option value="AZ">Arizona</option>
<option value="AR">Arkansas</option>
<option value="CA">California</option>
<option value="CO">Colorado</option>
<option value="CT">Connecticut</option>
<option value="DE">Delaware</option>
<option value="FL">Florida</option>

<option value="GA">Georgia</option>
<option value="HI">Hawaii</option>
<option value="ID">Idaho</option>
<option value="IL">Illinois</option>
<option value="IN">Indiana</option>
<option value="IA">Iowa</option>
<option value="KS">Kansas</option>
<option value="KY">Kentucky</option>
<option value="LA">Louisiana</option>

<option value="ME">Maine</option>
<option value="MD">Maryland</option>
<option value="MA">Massachusetts</option>
<option value="MI">Michigan</option>
<option value="MN">Minnesota</option>
<option value="MS">Mississippi</option>
<option value="MO">Missouri</option>
<option value="MT">Montana</option>
<option value="NE">Nebraska</option>

<option value="NV">Nevada</option>
<option value="NH">New Hampshire</option>
<option value="NJ">New Jersey</option>
<option value="NM">New Mexico</option>
<option value="NY">New York</option>
<option value="NC">North Carolina</option>
<option value="ND">North Dakota</option>
<option value="OH" selected>Ohio</option>
<option value="OK">Oklahoma</option>

<option value="OR">Oregon</option>
<option value="PA">Pennsylvania</option>
<option value="RI">Rhode Island</option>
<option value="SC">South Carolina</option>
<option value="SD">South Dakota</option>
<option value="TN">Tennessee</option>
<option value="TX">Texas</option>
<option value="UT">Utah</option>
<option value="VT">Vermont</option>

<option value="VA">Virginia</option>
<option value="WA">Washington</option>
<option value="WV">West Virginia</option>
<option value="WI">Wisconsin</option>
<option value="WY">Wyoming</option>
</select>
</div>
</p>
</body>
</html>
12
Mar
11

ExtJS GridPanel RowExpander With Ajax Call

This page describes the process of creating an extJS grid panel where each row can be expanded to reveal additional information. The additional information will be retrieved using Ext.Ajax.request.

Requirements

  • Text Editor
  • HTML Web Server

Procedure

There are 2 techniques to get the data:

  1. Load the complete set of data initially and have the row expander simply display it. This is explained in Part I
  2. Allow for lazy loading of the the data when the row is expanded by making an Ajax request. This is explained in Part II

The advantage of the first method is that there is almost zero delay expanding the rows since the data is retrieved ahead of time. The disadvantage is that most of the data that is downloaded may never be displayed

In the second method only the necessary data is retrieved from the server at the cost of the extra time required to make the additional request for data. You will note that things start changing from line 20 to line 50. The remaining code is the same as above.

Part I

Navigate to an empty directory and create the following file.

index.html

<html>
<head>

<link rel="stylesheet" type="text/css"
    href="http://dev.sencha.com/deploy/ext-3.3.1/resources/css/ext-all.css"/>

<script type="text/javascript"
    src="http://dev.sencha.com/deploy/ext-3.3.1/adapter/ext/ext-base-debug.js">
</script>
<script type="text/javascript"
    src="http://dev.sencha.com/deploy/ext-3.3.1/ext-all-debug.js">
</script>
    <script type="text/javascript" src="http://dev.sencha.com/deploy/ext-3.3.1/examples/ux/RowExpander.js"></script>


<script type="text/javascript">

function buildWindow() {
var myColumns = [];


// row expander
var expander = new Ext.ux.grid.RowExpander({
    tpl : new Ext.Template(
        '<p>&nbsp;&nbsp;<b>Age:</b> {age}</p><br>'
    )
});

var myStore = new Ext.data.JsonStore({
	url: 'response2.do',
	autoLoad : false, // we will manually call the load function.
	listeners: {
		'load' : function () {
			myStore.each(function(r) {
				myColumns.push(expander);
				// Full Name			
				var this_column = {};		
				r.fields.each(function(currentField) { 
					if(currentField.name == 'fullName') {
						this_column['header'] = 'Full Name';
						this_column['sortable'] = true;
						this_column['dataIndex'] = 'fullName';
						myColumns.push(this_column); 
						return false; // used as a break statement
					}
				});

				// State
				this_column = {};		
				r.fields.each(function(currentField) { 				
					// Ext.grid.Column API docs has complete attribute list.
					if(currentField.name == 'state') {
						this_column['header'] = 'State';
						this_column['sortable'] = true;
						this_column['dataIndex'] = 'state';
						myColumns.push(this_column); 
						return false; // used as a break statement
					}
				});
																	
				return false; // used as a break statement			
			});
			
		}
	}            
});

var grid = new Ext.grid.GridPanel({
	title : 'Name State Grid',
	store : myStore,
	autoHeight : true,
	plugins: expander,
	cm : new Ext.grid.ColumnModel({}), // Empty columnModel initially.
	renderTo : Ext.getBody()
});

// load the data and perform a reconfigure in the callback.
myStore.load({ 
	callback : function(r, options, success) { 
		var myColumnModel = new Ext.grid.ColumnModel({
			columns : myColumns
		});
		grid.reconfigure(myStore, myColumnModel);			
	}
});

}
Ext.onReady(buildWindow);
</script>
 
</head>
<body>
</body>
</html>

Data for Part I

The following data is provided to the component in Part I of this page. It returns all the information necessary to display the row including the data for the expanded state.

response2.do

{ 
"totalCount" : "4",
"records" : [
    {"state":"NY","fullName":"Ted Williams", "age" : 25},
    {"state":"NJ","fullName":"Bob Smith", "age" : 37},
    {"state":"NY","fullName":"John Adams", "age" : 44},
    {"state":"CA","fullName":"John Doe", "age" : 47}
],
"metaData" : {
	"totalProperty" : 'totalCount',
	"root" : 'records',
	"fields" : ['state', 'fullName', 'age']	
}
}

Part II

This part explains how to take the above grid and make it retrieve additional data once the row is expanded.

Steps:

  1. The first step was to create a blank expander. Unlike the example above the expander will be filled with data on the “beforeexpand” event.
  2. An event listener defined on the “beforeexpand” event makes an Ajax request. The URL for the request is customized based on the row that was clicked on.
  3. The data is decoded and the body of the row is overwritten using a Ext.layout.Template

The highlighted lines below are the ones that have been changed.

index2.html

<html>
<head>

<link rel="stylesheet" type="text/css"
    href="http://dev.sencha.com/deploy/ext-3.3.1/resources/css/ext-all.css"/>

<script type="text/javascript"
    src="http://dev.sencha.com/deploy/ext-3.3.1/adapter/ext/ext-base-debug.js">
</script>
<script type="text/javascript"
    src="http://dev.sencha.com/deploy/ext-3.3.1/ext-all-debug.js">
</script>
    <script type="text/javascript" src="http://dev.sencha.com/deploy/ext-3.3.1/examples/ux/RowExpander.js"></script>


<script type="text/javascript">

function buildWindow() {
var myColumns = [];

var myTemplate = new Ext.Template(
		'<p>&nbsp;&nbsp;<b>Age:</b> {age}</p><br>',
		'<p>&nbsp;&nbsp;<b>Address:</b> {address}</p><br>',
		'<p>&nbsp;&nbsp;<b>City:</b> {city}</p><br>',
		'<p>&nbsp;&nbsp;<b>Zip:</b> {zip}</p><br>'
	);

// row expander
var expander = new Ext.ux.grid.RowExpander({});

expander.on('beforeexpand', function( rowexpander, record, body, rowindex) {
	// make the ajax request
    Ext.Ajax.request({
        url : 'app/users/' + record.get('id') ,
        method: 'GET',
        success: function ( result, request ) {
			// retrive the data
	        var jsonData = Ext.util.JSON.decode(result.responseText);	        
			// pass the data to the overwrite function.
			myTemplate.overwrite(body, jsonData);
        },
        failure: function ( result, request) {
            Ext.MessageBox.alert('Failed', result.responseText);
            return false;
        }
    });
	
	return true;
});

var myStore = new Ext.data.JsonStore({
	url: 'response2.do',
	autoLoad : false, // we will manually call the load function.
	listeners: {
		'load' : function () {
			myStore.each(function(r) {
				myColumns.push(expander);
				// Full Name			
				var this_column = {};		
				r.fields.each(function(currentField) { 
					if(currentField.name == 'fullName') {
						this_column['header'] = 'Full Name';
						this_column['sortable'] = true;
						this_column['dataIndex'] = 'fullName';
						myColumns.push(this_column); 
						return false; // used as a break statement
					}
				});

				// State
				this_column = {};		
				r.fields.each(function(currentField) { 				
					// Ext.grid.Column API docs has complete attribute list.
					if(currentField.name == 'state') {
						this_column['header'] = 'State';
						this_column['sortable'] = true;
						this_column['dataIndex'] = 'state';
						myColumns.push(this_column); 
						return false; // used as a break statement
					}
				});
																	
				return false; // used as a break statement			
			});
			
		}
	}            
});

var grid = new Ext.grid.GridPanel({
	title : 'Name State Grid',
	store : myStore,
	autoHeight : true,
	plugins: expander,
	cm : new Ext.grid.ColumnModel({}), // Empty columnModel initially.
	renderTo : Ext.getBody()
});

// load the data and perform a reconfigure in the callback.
myStore.load({ 
	callback : function(r, options, success) { 
		var myColumnModel = new Ext.grid.ColumnModel({
			columns : myColumns
		});
		grid.reconfigure(myStore, myColumnModel);			
	}
});

}
Ext.onReady(buildWindow);
</script>
 
</head>
<body>
</body>
</html>

Data for Part II

The following files simulate data being returned by a server side resource (Servlet or RESTlet). You can create them using a standard text editor. The files below don’t have an extension so keep that in mind when creating these files in windows.

Note: The information for User 4 is missing on purpose. This is to show the message that would display if information about a user was not found.

response2.do

{ 
"totalCount" : "4",
"records" : [
    {"id" : 1, "state":"NY","fullName":"Ted Williams", "age" : 25},
    {"id" : 2, "state":"NJ","fullName":"Bob Smith", "age" : 37},
    {"id" : 3, "state":"NY","fullName":"John Adams", "age" : 44},
    {"id" : 4, "state":"CA","fullName":"John Doe", "age" : 47}
],
"metaData" : {
	"totalProperty" : 'totalCount',
	"root" : 'records',
	"fields" : ['id', 'state', 'fullName', 'age']	
}
}

app/users/1

{ 
"age" : "45",
"address" : "145-30 Chestnut Street",
"city" : "Rutherford, CT",
"zip" : 10010
}

app/users/2

{ 
"age" : "46",
"address" : "145-31 Chestnut Street",
"city" : "Rutherford, CT",
"zip" : 10010
}

app/users/3

{ 
"age" : "23",
"address" : "145-32 Chestnut Street",
"city" : "Rutherford, CT",
"zip" : 10010
}

That’s All for now!

05
Mar
11

ExtJS Dynamic Grid Columns

This page describes the process of dynamically creating a ExtJS columnModel based on data from a Ajax call. Generating a data Grid when the columns can not be predefined requires a different approach. The data coming from the server is evaluated and the columnModel is constructed in a set of if-else conditions. Once the column model and store have been initialized they are passed to the gridpanel for display.

Requirements

  • Html Editor
  • HTTP Server to serve the pages from

Procedure

Create the following html page:

Procedure:

  1. Make an Ajax request using the Standard XMLHttpRequest browser object.
  2. Read the meta-data from the response
  3. Run thru a series of if-else conditions to create and insert columns into the dynamic column model
  4. Feed the GridPanel with the store and the column model.

The following html file sets up the styles and imports the core JavaScript libraries.

index.html

<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Stateful + Dynamic Grid Panel with Grouping and Totals</title>


    <!-- ** CSS ** -->
    <!-- base library -->
    <link rel="stylesheet" type="text/css" href="http://dev.sencha.com/deploy/ext-3.3.1/resources/css/ext-all.css" />

    <!-- overrides to base library -->

    <!-- page specific -->
    <link rel="stylesheet" type="text/css" href="http://dev.sencha.com/deploy/ext-3.3.1/examples/shared/examples.css" />

    <style type=text/css>
/*!
 * Ext JS Library 3.3.1
 * Copyright(c) 2006-2010 Sencha Inc.
 * licensing@sencha.com
 * http://www.sencha.com/license
 */
#grid-example .x-grid-col-1 {
	text-align: right;
}
#grid-example .x-grid-col-2{
	text-align: right;
}
#grid-example .x-grid-col-3 {
	text-align: right;
}
#grid-example .x-grid-col-4 {
	text-align: right;
}
#grid-example.x-grid-mso{
	border: 1px solid #6593cf;
}
#grid-example.x-grid-vista{
	border: 1px solid #b3bcc0;
}
#xml-grid-example{
	border: 1px solid #cbc7b8;
	left: 0;
	position: relative;
	top: 0;
}
#editor-grid .x-grid-col-2{
    text-align:right;
}
.x-grid3-td-topic b {
    font-family:tahoma, verdana;
    display:block;
}
.x-grid3-td-topic b i {
    font-weight:normal;
    font-style: normal;
    color:#000;
}
.x-grid3-td-topic .x-grid3-cell-inner {
    white-space:normal;
}
.x-grid3-td-topic a {
    color: #385F95;
    text-decoration:none;
}
.x-grid3-td-topic a:hover {
    text-decoration:underline;
}
.details .x-btn-text {
    background-image: url(details.gif);
}
.x-resizable-pinned .x-resizable-handle-south{
    background:url(../../resources/images/default/sizer/s-handle-dark.gif);
    background-position: top;
}
        /* style rows on mouseover */
        .x-grid3-row-over .x-grid3-cell-inner {
            font-weight: bold;
        }

        /* style for the "buy" ActionColumn icon */
        .x-action-col-cell img.buy-col {
            height: 16px;
            width: 16px;
            background-image: url(../shared/icons/fam/accept.png);
        }

        /* style for the "alert" ActionColumn icon */
        .x-action-col-cell img.alert-col {
            height: 16px;
            width: 16px;
            background-image: url(../shared/icons/fam/error.png);
        }

    </style>
    
    <!-- overrides to base library -->
    <link rel="stylesheet" type="text/css" href="http://dev.sencha.com/deploy/ext-3.3.1/examples/ux/css/GroupSummary.css" />

    <!-- ** Javascript ** -->

    <!-- ExtJS library: base/adapter -->
<script type="text/javascript"
    src="http://dev.sencha.com/deploy/ext-3.3.1/adapter/ext/ext-base-debug.js">
</script>
<script type="text/javascript"
    src="http://dev.sencha.com/deploy/ext-3.3.1/ext-all-debug.js">
</script>

    <!-- overrides to base library -->
    <!-- extensions -->
    <script type="text/javascript" src="http://dev.sencha.com/deploy/ext-3.3.1/examples/ux/GroupSummary.js"></script>

    <!-- page specific -->

    <script type="text/javascript" src="array-grid.js"></script>

</head>
<body>
    <h1>Stateful + Dynamic Grid Panel with Grouping and Totals</h1>
    <p>Note that the js is not minified so it is readable. See <a href="array-grid.js">array-grid.js</a>.</p>
    
    <div id="grid-example2"></div>

</body>
</html>

array-grid.js

Ext.onReady(function(){
    Ext.QuickTips.init();
    Ext.state.Manager.setProvider(new Ext.state.CookieProvider());

	// The columns are constructed dynamically here 
	// Ext.Ajax.request() introduces timing issues since it is asynchronous.
	// use XMLHttpRequest since it is synchronous in nature.
    var xmlhttp = {};
    if (window.XMLHttpRequest) {// code for IE7+, Firefox, Chrome, Opera, Safari
      xmlhttp=new XMLHttpRequest();
    } else { // code for IE6, IE5
        xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
    }
    xmlhttp.open("GET",'response1.do',false); // make a synchronous request
    xmlhttp.send(null);

	var prefs = {};
    if(xmlhttp.status == 200) {
	    var ajaxReader = new Ext.data.JsonReader();
		var ajaxStore = new Ext.data.GroupingStore({
			groupField:'industry',
			reader: ajaxReader,
			data: Ext.decode(xmlhttp.responseText)
		});

		var myColumns = [];
		// create the column model dynamically.
		for (var i = 0, len = ajaxReader.meta.fields.length; i < len; i++) {
			var field = ajaxReader.meta.fields[i];
			if(field.name == 'company') {
 				var this_column = {};
                this_column['id'] = 'company';
                this_column['header'] = 'Company';
                this_column['width'] = 60;
                this_column['sortable'] = true;
                this_column['dataIndex'] = 'company';
                this_column['hideable'] = false;
                this_column['summaryType'] = 'max';                    
                myColumns.push(this_column); 					
			} else if(field.name == 'price') {
                var this_column = {};
                this_column['id'] = 'price';
                this_column['header'] = 'Price';
                this_column['width'] = 20;
                this_column['sortable'] = true;
                this_column['dataIndex'] = 'price';
				this_column['renderer'] = Ext.util.Format.usMoney;
                this_column['summaryType'] = 'average';
                myColumns.push(this_column); 				
			} else if (field.name == 'change') {
				// Change 				
                var this_column = {};
                this_column['id'] = 'change';
                this_column['header'] = 'Change';
                this_column['width'] = 20;
                this_column['sortable'] = true;
				this_column['renderer'] = Ext.util.Format.usMoney;
                this_column['dataIndex'] = 'change';
                this_column['summaryType'] = 'max';
                myColumns.push(this_column); 				
			} else if (field.name == 'industry') {                    
				// Industry
                var this_column = {};
                this_column['id'] = 'industry';
                this_column['header'] = 'Industry';
                this_column['width'] = 20;
                this_column['sortable'] = true;
                this_column['dataIndex'] = 'industry';
                myColumns.push(this_column);
			} else if (field.name == 'lastChange') {                    
				// Last Updated
                var this_column = {};
                this_column['id'] = 'lastChange';
                this_column['header'] = 'Last Change';
                this_column['width'] = 20;
                this_column['sortable'] = true;
                this_column['dataIndex'] = 'lastChange';
                this_column['summaryType'] = 'max';
                myColumns.push(this_column);
			}	 					 				
		}					
		var grid2 = new Ext.grid.GridPanel({
			store: ajaxStore,
			stripeRows: true,
			height: 350,
			width: 600,
			stateful: true,
			stateId: 'grid2',
	        plugins: new Ext.ux.grid.GroupSummary(),
	        title: 'Stateful + Dynamic Grid Panel with Grouping and Totals',				
			view: new Ext.grid.GroupingView({
			    forceFit:true,
			    groupTextTpl: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'
			}),
			fbar  : ['->', {
			    text:'Clear Grouping',
			    iconCls: 'icon-clear-group',
			    handler : function(){
			        ajaxStore.clearGrouping();
			        grid2.fireEvent('groupchange', this);
			    }
			}],        
			cm: new Ext.grid.ColumnModel({
				defaults: {
					width: 120,
					sortable: true
				},
				columns: myColumns
			})		
		});
		grid2.render('grid-example2'); 				 				
		
    } else {
        // there was an error
//        console.error('there was an error getting data from the server');
    } 
});

The “metaData” in the response below allows the JsonReader to configure itself.

response1.do

{
"metaData" : {
	totalProperty : 'totalCount',
	root : 'records',
	successProperty : "success",
	fields : [
       {name: 'company', type : 'string'},
       {name: 'price', type: 'float'},
       {name: 'change', type: 'float'},
       {name: 'pctChange', type: 'float'},
       {name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'},
       {name: 'industry', type : 'string'}
	]
},
"success": true,
"totalCount" : "30",
"records" : [
    {company: '3m Co'								,price: 71.72,change: 0.02,pctChange: 0.03,lastChange: '4/2 12:00am', industry: 'Manufacturing'},
    {company: 'Alcoa Inc'							,price: 29.01,change: 0.42,pctChange: 1.47,lastChange: '4/1 12:00am', industry: 'Manufacturing'},
    {company: 'Altria Group Inc'					,price: 83.81,change: 0.28,pctChange: 0.34,lastChange: '4/3 12:00am', industry: 'Manufacturing'},
    {company: 'American Express Company'			,price: 52.55,change: 0.01,pctChange: 0.02,lastChange: '4/8 12:00am', industry: 'Finance'},
    {company: 'American International Group, Inc.'	,price: 64.13,change: 0.31,pctChange: 0.49,lastChange: '4/1 12:00am', industry: 'Services'},
    {company: 'AT&T Inc.'							,price: 31.61,change: -0.48,pctChange: -1.54,lastChange: '4/8 12:00am', industry: 'Services'},
    {company: 'Boeing Co.'							,price: 75.43,change: 0.53,pctChange: 0.71,lastChange: '4/8 12:00am', industry: 'Manufacturing'},
    {company: 'Caterpillar Inc.'					,price: 67.27,change: 0.92,pctChange: 1.39,lastChange: '4/1 12:00am', industry: 'Services'},
    {company: 'Citigroup, Inc.'						,price: 49.37,change: 0.02,pctChange: 0.04,lastChange: '4/4 12:00am', industry: 'Finance'},
    {company: 'E.I. du Pont de Nemours and Company'	,price: 40.48,change: 0.51,pctChange: 1.28,lastChange: '4/1 12:00am', industry: 'Manufacturing'},
    {company: 'Exxon Mobil Corp'					,price: 68.1,change: -0.43,pctChange: -0.64,lastChange: '4/3 12:00am', industry: 'Manufacturing'},
    {company: 'General Electric Company'			,price: 34.14,change: -0.08,pctChange: -0.23,lastChange: '4/3 12:00am', industry: 'Manufacturing'},
    {company: 'General Motors Corporation'			,price: 30.27,change: 1.09,pctChange: 3.74,lastChange: '4/3 12:00am', industry: 'Automotive'},
    {company: 'Hewlett-Packard Co.'					,price: 36.53,change: -0.03,pctChange: -0.08,lastChange: '4/3 12:00am', industry: 'Computer'},
    {company: 'Honeywell Intl Inc'					,price: 38.77,change: 0.05,pctChange: 0.13,lastChange: '4/3 12:00am', industry: 'Manufacturing'},
    {company: 'Intel Corporation'					,price: 19.88,change: 0.31,pctChange: 1.58,lastChange: '4/2 12:00am', industry: 'Computer'},
    {company: 'International Business Machines'		,price: 81.41,change: 0.44,pctChange: 0.54,lastChange: '4/1 12:00am', industry: 'Computer'},
    {company: 'Johnson & Johnson'					,price: 64.72,change: 0.06,pctChange: 0.09,lastChange: '4/2 12:00am', industry: 'Medical'},
    {company: 'JP Morgan & Chase & Co'				,price: 45.73,change: 0.07,pctChange: 0.15,lastChange: '4/2 12:00am', industry: 'Finance'},
    {company: 'McDonald\'s Corporation'				,price: 36.76,change: 0.86,pctChange: 2.40,lastChange: '4/2 12:00am', industry: 'Food'},
    {company: 'Merck & Co., Inc.'					,price: 40.96,change: 0.41,pctChange: 1.01,lastChange: '4/2 12:00am', industry: 'Medical'},
    {company: 'Microsoft Corporation'				,price: 25.84,change: 0.14,pctChange: 0.54,lastChange: '4/2 12:00am', industry: 'Computer'},
    {company: 'Pfizer Inc'							,price: 27.96,change: 0.4,pctChange: 1.45,lastChange: '4/8 12:00am', industry: 'Services'},
    {company: 'The Coca-Cola Company'				,price: 45.07,change: 0.26,pctChange: 0.58,lastChange: '4/1 12:00am', industry: 'Food'},
    {company: 'The Home Depot, Inc.'				,price: 34.64,change: 0.35,pctChange: 1.02,lastChange: '4/8 12:00am', industry: 'Retail'},
    {company: 'The Procter & Gamble Company'		,price: 61.91,change: 0.01,pctChange: 0.02,lastChange: '4/1 12:00am', industry: 'Manufacturing'},
    {company: 'United Technologies Corporation'		,price: 63.26,change: 0.55,pctChange: 0.88,lastChange: '4/1 12:00am', industry: 'Computer'},
    {company: 'Verizon Communications'				,price: 35.57,change: 0.39,pctChange: 1.11,lastChange: '4/3 12:00am', industry: 'Services'},
    {company: 'Wal-Mart Stores, Inc.'				,price: 45.45,change: 0.73,pctChange: 1.63,lastChange: '4/3 12:00am', industry: 'Retail'},
    {company: 'Walt Disney Company (The) (Holding Company)',price: 29.89,change: 0.24,pctChange: 0.81,lastChange: '4/1 12:00am', industry: 'Services'}
]
}

Test the page

The page will NOT work if you simply save the files to a local directory and point your browser there. The pages must be served by a HTTP Server. Setting up a server is beyond the scope of this page.

05
Mar
11

Displaying Data on an extJS Grid

This page describes how to display JSON data from an Ajax call on a extjs grid panel.

Requirements

  • html editor
  • Access to a HTTP Server

Start in an empty directory and create the following file.

index.html

<html>
<head>

<link rel="stylesheet" type="text/css"
    href="http://dev.sencha.com/deploy/ext-3.3.1/resources/css/ext-all.css"/>

<script type="text/javascript"
    src="http://dev.sencha.com/deploy/ext-3.3.1/adapter/ext/ext-base-debug.js">
</script>
<script type="text/javascript"
    src="http://dev.sencha.com/deploy/ext-3.3.1/ext-all-debug.js">
</script>

<script type="text/javascript">

function buildWindow() {

var grid = new Ext.grid.GridPanel({
	title : 'Name State Grid',
	store : new Ext.data.JsonStore({
		url: 'response1.do',
		autoLoad : 'true',
		totalProperty: 'totalCount',
		id : 'state',
		root: 'records',
		fields: ['state', 'fullName']
	}),
	autoHeight : true,
	cm : new Ext.grid.ColumnModel( [ {
        header : 'Full Name',
        sortable : true,
        dataIndex : 'fullName'
    }, {
        header : 'State',
        sortable : true,        
        dataIndex : 'state'
    } ]),
	renderTo : Ext.getBody()
});

}
Ext.onReady(buildWindow);
</script>
 
</head>
<body>
</body>
</html>

response1.do

{ 
"totalCount" : "4",
"records" : [
    {"state":"NY","fullName":"Ester Jones"},
    {"state":"NJ","fullName":"Bob Smith"},
    {"state":"NY","fullName":"John Adams"},
    {"state":"CA","fullName":"John Doe"}
]}
21
Sep
10

Direct Web Remoting (DWR) Hello World Example

This page describes the process of setting up a very simple hello world application using direct web remoting (DWR) Java library. At the end of this you will have a working DWR enabled application running in jetty.

Background

DWR is used along with other AJAX technologies to allow for creation of rich internet applications. Often times you will find yourself using frameworks like Dojo or extJS to display the data. If your working with Java, getting the data to those frameworks often involves working with JSON or XML data and writing Servlets or something similar. DWR allows you avoid the pain and hassle of all that and allows you to directly surface your data from Java Code (possibly SpringBeans). DWR does this by wrapping your Java Beans with Javascript Objects. These objects make Ajax calls to your back end Java code right from your html page. Since these are asynchronous Javascript calls, data from your Java code can be used to update your html page without requiring your web page be refreshed.

Requirements

Procedure

We start by generating the web application using Maven 2.

mvn archetype:generate -DarchetypeArtifactId=maven-archetype-webapp

Answer the questions like seen below.

[INFO] Generating project in Interactive mode
Define value for groupId: : com.test
Define value for artifactId: : dwrHelloWorld
Define value for version:  1.0-SNAPSHOT: :
Define value for package:  com.test: :
Confirm properties configuration:
groupId: com.test
artifactId: dwrHelloWorld
version: 1.0-SNAPSHOT
package: com.test
 Y: : Y

Hit enter for the rest of the defaults.

cd to the project’s folder and modify the pom.xml so that it looks like the one below.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.test</groupId>
	<artifactId>dwrHelloWorld</artifactId>
	<packaging>war</packaging>
	<version>1.0-SNAPSHOT</version>
	<name>dwrHelloWorld 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>org.directwebremoting</groupId>
			<artifactId>dwr</artifactId>
			<version>2.0.3</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.1.2</version>
		</dependency>
		<dependency>
			<groupId>taglibs</groupId>
			<artifactId>standard</artifactId>
			<version>1.1.2</version>
		</dependency>
		<dependency>
			<groupId>org.apache.geronimo.specs</groupId>
			<artifactId>geronimo-servlet_2.5_spec</artifactId>
			<version>1.2</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.15</version>
			<exclusions>
				<exclusion>
					<groupId>javax.mail</groupId>
					<artifactId>mail</artifactId>
				</exclusion>
				<exclusion>
					<groupId>javax.jms</groupId>
					<artifactId>jms</artifactId>
				</exclusion>
				<exclusion>
					<groupId>com.sun.jdmk</groupId>
					<artifactId>jmxtools</artifactId>
				</exclusion>
				<exclusion>
					<groupId>com.sun.jmx</groupId>
					<artifactId>jmxri</artifactId>
				</exclusion>
				<exclusion>
					<groupId>javax.activation</groupId>
					<artifactId>activation</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>commons-logging</groupId>
			<artifactId>commons-logging</artifactId>
			<version>1.0.4</version>
		</dependency>

	</dependencies>
	<build>
		<finalName>dwrHelloWorld</finalName>
		<plugins>
			<plugin>
				<groupId>org.mortbay.jetty</groupId>
				<artifactId>jetty-maven-plugin</artifactId>
				<version>7.0.0.v20091005</version>
				<configuration>
					<scanIntervalSeconds>2</scanIntervalSeconds>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

Next we create the src/main/java folder since this is not done for us using the archetype.

on unix you type: mkdir src/main/java

Next we will modify the web.xml file

src/main/webapp/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app 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"
	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">
	<display-name>dwrHelloWorld</display-name>

	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>
<servlet>
  <servlet-name>dwr-invoker</servlet-name>
  <display-name>DWR Servlet</display-name>
  <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
  <init-param>
     <param-name>debug</param-name>
     <param-value>true</param-value>
  </init-param>
</servlet>

<servlet-mapping>
  <servlet-name>dwr-invoker</servlet-name>
  <url-pattern>/dwr/*</url-pattern>
</servlet-mapping>

</web-app>

The following maps your java classs to the JavaScript object. The DWR library uses reflection to identify the public methods and creates javascript functions that wrap the functionality they provide.

src/main/webapp/WEB-INF/dwr.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dwr PUBLIC
    "-//GetAhead Limited//DTD Direct Web Remoting 2.0//EN"
    "http://getahead.org/dwr/dwr20.dtd">

<dwr>
  <allow>
    <create creator="new" javascript="HelloWorldModelImpl">
      <param name="class" value="com.test.HelloWorldModelImpl"/>
    </create>
  </allow>
</dwr>

src/main/java/com/test/HelloWorldModelImpl.java

public class HelloWorldModelImpl {
    public String getData() {
        return "Hello World";
    }
}

Next we will create the JSP’s used to display the pages.

src/main/webapp/index.jsp

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>Hello World with Direct Web Remoting</title>

<script type="text/javascript"
    src="${request.contextPath}/dwr/interface/HelloWorldModelImpl.js"> </script>
<script type="text/javascript"
    src="${request.contextPath}/dwr/engine.js"> </script>

</head>
<body>
<script type="text/javascript">
function handleGetData(str) {
  alert(str);
}

HelloWorldModelImpl.getData(handleGetData);
</script>
</body>
</html>

Test Your Application

Start up jetty and test your application

cd to your project’s base folder and type:

mvn jetty:run

navigate to http://localhost:8080/

You should see a Hello World alert pop-up on your screen.

Subscribe to this blog to get more articles on this topic in the future.

That’s all for now.

30
Dec
09

Listbox Picker Using JSF and Tomahawk

This page is about creating an HTML Switchlist or Picker using JSF and the Tomahawk library. We will start by creating a basic multiselect listbox and then convert that to a switchlist. If you have Maven installed you may follow along. The full program should not take more then 20 minutes to complete and have up and running.

Requirements

  • Maven
  • Eclipse or IDEA Java IDE
  • That’s about it :)

Step 1: Project Setup

cd to your workspace directory and create a new project using the maven archetype “maven-archetype-webapp”.

I did it the following way

mvn archetype:generate -DarchetypeArtifactId=maven-archetype-webapp

Answer the rest of the questions like this… hit enter for the defaults.

Define value for groupId: : com.test 
Define value for artifactId: : testSwitchList
Define value for version:  1.0-SNAPSHOT: : 
Define value for package:  com.test: : 
Confirm properties configuration:
groupId: com.test
artifactId: testSwitchList
version: 1.0-SNAPSHOT
package: com.test
 Y: : 

Make sure you Answer the highlighted items exactly as above. The rest of the page depends on this.

cd testSwitchList; mvn eclipse:clean eclipse:eclipse (if you have IDEA then mvn idea:idea)

Import the project Into your IDE.

The pom.xml file should look like this…

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.test</groupId>
  <artifactId>testSwitchList</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>testSwitchList 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>org.apache.myfaces.core</groupId>
    <artifactId>myfaces-api</artifactId>
    <version>1.2.8</version>
</dependency>

<dependency>
    <groupId>org.apache.myfaces.tomahawk</groupId>
    <artifactId>tomahawk</artifactId>
    <version>1.1.9</version>
</dependency>

<dependency>
    <groupId>org.apache.myfaces.core</groupId>
    <artifactId>myfaces-impl</artifactId>
    <version>1.2.8</version>
</dependency>
<dependency>
    <groupId>org.apache.geronimo.specs</groupId>
    <artifactId>geronimo-servlet_2.5_spec</artifactId>
    <version>1.2</version>
    <type>jar</type>
    <scope>provided</scope>
</dependency>
  </dependencies>
  <build>
    <finalName>testSwitchList</finalName>
<plugins>
    <plugin>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>jetty-maven-plugin</artifactId>
                <version>7.0.0pre1</version>
        <configuration>
            <scanIntervalSeconds>2</scanIntervalSeconds>
        </configuration>
    </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.0.2</version>
                <configuration>
                    <source>1.5</source>
                    <target>1.5</target>
                </configuration>
            </plugin>
</plugins>
  </build>
</project>

Create a folder named java in the src/main directory of your project. By default this archetype does not create one.

Regenerate the eclipse or idea project (ex. mvn eclipse:clean eclipse:eclipse) and refresh the project in your IDE.

Step 2: Configuration

Replace the web.xml file in the src/main/webapp/WEB-INF folder with this:

<?xml version="1.0" encoding="UTF-8"?>
<web-app 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"
	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">
	<display-name>Archetype Created Web Application</display-name>

	<servlet>
		<servlet-name>Faces Servlet</servlet-name>
		<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>Faces Servlet</servlet-name>
		<url-pattern>*.jsf</url-pattern>
	</servlet-mapping>
	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>
</web-app>

faces-config.xml

Create a faces-config.xml file in the src/main/webapp/WEB-INF folder.

<?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">

	<converter>
	    <converter-for-class>com.test.Genre</converter-for-class>
	    <converter-class>com.test.GenreConverter</converter-class>
	</converter>

	<managed-bean>
		<managed-bean-name>genreBean</managed-bean-name>
		<managed-bean-class>com.test.GenreBean</managed-bean-class>
		<managed-bean-scope>session</managed-bean-scope>
	</managed-bean>

</faces-config>

Implementation

The following is the Genre Entity Class.

src/main/java/com/test/Genre.java

package com.test;

import java.io.Serializable;

public class Genre implements Serializable {
	private static final long serialVersionUID = 1L;
	private Integer id;
	private String name;

	public Genre() {
		super();
	}

	public Genre(Integer id, String name) {
		this.id = id;
		this.name = name;
	}

	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public String toString() {
		return "Genre [id=" + id + ", name=" + name + "]";
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Genre other = (Genre) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id)) {
			return false;
		}
		return true;
	}
}

The following is the Genre Managed Bean.

src/main/java/com/test/GenreBean.java

package com.test;

import java.util.ArrayList;
import java.util.List;

import javax.faces.model.SelectItem;

public class GenreBean {
    private Genre[] selectedDataList;

	public Genre[] getSelectedDataList() {
		return selectedDataList;
	}

	public void setSelectedDataList(Genre[] selectedDataList) {
		this.selectedDataList = selectedDataList;
	}

    public List<SelectItem> getSelectItemsStatic() {
        List<SelectItem> selectItems = new ArrayList<SelectItem>();
        for (Genre genre : GenreBean.getAllGenreStatic()) {
            selectItems.add(new SelectItem(genre, genre.getName()));
        }
        return selectItems;
    }

    public static Genre getById(Integer id) {
    	List<Genre> list = getAllGenreStatic();
    	int index = list.indexOf(new Genre(id, "doesnt matter based on equals()"));
    	Genre genre = null;
    	if(index != -1) {
    		genre = list.get(index);
    	}
    	return genre;
    }

    private static List<Genre> getAllGenreStatic() {
    	List<Genre> list = new ArrayList<Genre>();
    	list.add(new Genre(1, "Action"));
    	list.add(new Genre(2, "Drama"));
    	list.add(new Genre(3, "Documentary"));
		return list;
	}

    public String countSelected() {
    	System.out.println("selected: " + selectedDataList.length);
    	selectedDataList = null;
    	return "delete";
    }
}

The following is the Converter

The converter helps to translate between the string representation of the object and the object itself. Typical objects like String dont need converters. Any object that is an entity that you want displayed in Any JSF component needs a converter. Below you see an object that converts the Genre bean from a string representation to an actual Genre bean. In reality you would replace the source of the data to be a object cache or database datamanager. But for now we are using hard coded data in the GenreBean.

src/main/java/com/test/GenreConverter.java

package com.test;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;

public class GenreConverter implements Converter {

	/**
	 * This method is responsible for re-constructing the object from the text
	 * representation of the object from the HTML form element. Typically this
	 * method would use a datamanager that will "lookup" the information.
	 */
	@Override
	public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2)
			throws ConverterException {
		Genre genre = new Genre();
		if("".equals(arg2)) {
			return genre;
		}
		// typically would go to the database to get the object.
		Integer id = Integer.parseInt(arg2);
		// call a static method for now. But this really should be coming
		// from a datamanager.
		genre = GenreBean.getById(id);
		return genre;
	}

	@Override
	public String getAsString(FacesContext arg0, UIComponent arg1, Object arg2)
			throws ConverterException {		
		if(arg2 instanceof Genre) {
			Genre genre = (Genre)arg2;
			return ""+ genre.getId();
		}
		return "";
	}
}

The following is the JSP

/src/main/webapp/index.jsp

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

<f:view>
	<html>
	<head>
	<title>Hello World JSF Example</title>
	</head>
	<body>
	<h:form id="myform">
	<h3>Select Movie Genre</h3>
		<h:selectManyListbox id="genre_select"
			value="#{genreBean.selectedDataList}" size="5">
			<f:selectItems value="#{genreBean.selectItemsStatic}" />
		</h:selectManyListbox>
		<br />
		<h:commandButton action="#{genreBean.countSelected}" />
	</h:form>
	</body>
	</html>
</f:view>

test the application

mvn jetty:run

http://localhost:8080/testSwitchList/index.jsf

Convert to a Switch List

The final step is to convert the listbox to a switchlist. We are going to use the advanced features provided by the tomahawk jsf plugin to generate ourselves the switchlist as well as the javascript that supports it.

The old school method of doing this is described here. As you can see this process was tedious because the javascript had to be manually customized and some additional frameworks like JQuery needed to be used in order to make things easier.

On this page we will describe the easier way of getting this done with JSF.

Go ahead and add the following to the web.xml file.

<filter>
        <filter-name>MyFacesExtensionsFilter</filter-name>
        <filter-class>org.apache.myfaces.webapp.filter.ExtensionsFilter</filter-class>
</filter>

<!-- extension mapping for adding <script/>, <link/>, and other resource tags to JSF-pages  -->
<filter-mapping>
    <filter-name>MyFacesExtensionsFilter</filter-name>
    <!-- servlet-name must match the name of your javax.faces.webapp.FacesServlet entry -->
    <servlet-name>Faces Servlet</servlet-name>
</filter-mapping>

<!-- extension mapping for serving page-independent resources (javascript, stylesheets, images, etc.)  -->
<filter-mapping>
    <filter-name>MyFacesExtensionsFilter</filter-name>
    <url-pattern>/faces/myFacesExtensionResource/*</url-pattern>
</filter-mapping>

Include the following up top in the jsp file.

<%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%>

Simply changing the h:selectManyListbox to the t:selectManyPicklist enables the switchlist functionality on the page.

		<t:selectManyPicklist id="genre_select"
			value="#{genreBean.selectedDataList}" size="5">
			<f:selectItems value="#{genreBean.selectItemsStatic}" />
		</t:selectManyPicklist>

The selectManyPicklist imports all the necessary javascript on the page in addition to the command buttons that let you move items from left to right.

test the application

mvn jetty:run

http://localhost:8080/testSwitchList/index.jsf

By viewing the source for the page you notice that most of the coding is already done for you. JSF makes all this easy.

Thats all for now!

29
Dec
09

Listbox Picker using Javascript

This page is about creating an HTML Switchlist or Picker using Javascript.

Background

Listbox pickers are 2 listboxes with left move and right move buttons in between them. Items in each listbox can be added or removed from each other.

Requirements

  • Basic Text Editor
  • HTML Browser

Implementation

In order to accomplish this the 2 lists are displayed as html select elements. Each item on the list is an option element. When the user clicks on one of the 2 buttons a javascript call is made.

The javascript is responsible for moving selected items from the source to the target select element. The javascript function goes thru the following  process.

  1. Create a temporary arrays and a map for the source and target select boxes.
  2. Copy all the elements from the target listbox to the target temp array
  3. Copy all the selected items from the source listbox to the temp target array.
  4. Copy all the unselected elements from the source listbox to the source temp array.
  5. Clear out the listboxes and regenerate them using the target and source arrays.
  6. Assign the values of each element using the map structure.
<script language="JavaScript" type="text/JavaScript">
function moveItems(srcSelect, targetSelect) {
	var srcTmpArr = new Array();
	var targetTmpArr = new Array();
	// map type structure that stores the value of a entry given its display text.
	var valStoreMap = new Array();
	var i;
	// iterate thru the target select box and store all the values and
	// display text into a Map type structure. All values because this
	// is the target.
	for (i = 0; i < targetSelect.options.length; i++) {
		// need to hold on to value of item so we load up the map with value from target.
		valStoreMap[targetSelect.options[i].text] = targetSelect.options[i].value;
		targetTmpArr[i] = targetSelect.options[i].text;
	}
	// get the length of the target Arr that we populated above.
	// this is faster than doing a targetLength++ each time in the loop.
	var targetLength = targetTmpArr.length;

	// next we start working on the source array. This is tricky since
	// the selected items in the list will need to be MOVED over to
	// the target array. Unselected items remain behind.

	// start from a fresh source array.
	var srcLength = 0;
	// iterate thru the source structure and store all the values.
	for (i = 0; i < srcSelect.options.length; i++) {
		// need to hold on to value of item so we load up the map with value from source.
		valStoreMap[srcSelect.options[i].text] = srcSelect.options[i].value;

		// if something in the source is selected and its not blank then
		// copy it to the target Temp Array.
		if (srcSelect.options[i].selected && srcSelect.options[i].value != "") {
			targetTmpArr[targetLength] = srcSelect.options[i].text;
			targetLength++;
		} else {
			// else just add it to the source Temp array as if nothing happend.
			srcTmpArr[srcLength] = srcSelect.options[i].text;
			srcLength++;
		}
	}
	srcTmpArr.sort();
	targetTmpArr.sort();
	// reset the source and target select boxes to prepare them for copy.
	srcSelect.length = 0;
	targetSelect.length = 0;

	// populate the select boxes with the new values from the temp arrays.
	var i;
	for (i = 0; i < srcTmpArr.length; i++) {
		var optionItem = new Option();
		// lookup the value of the option Item from the map
		optionItem.value = valStoreMap[srcTmpArr[i]];
		optionItem.text = srcTmpArr[i];
		srcSelect[i] = optionItem;
	}
	for (i = 0; i < targetTmpArr.length; i++) {
		var optionItem = new Option();
		// lookup the value of the option Item from the map
		optionItem.value = valStoreMap[targetTmpArr[i]];
		optionItem.text = targetTmpArr[i];
		targetSelect[i] = optionItem;
	}
}

function selectAll(box) {
	for (var i = 0; i < box.length; i++) {
		box[i].selected = true;
	}
}
</script>

The following is the code to generate the 2 listboxes and the buttons between them.

		<table border="0" cellspacing="2" cellpadding="0">
			<tr align="center" valign="middle">
				<td align="right"><select name="FirstList" size="15" multiple
					id="FirstList" style="width: 250">
					<option value="A">Bob</option>
					<option value="B">Ted</option>
					<option value="C">John</option>
					<option value="D">Greg</option>
					<option value="E">Steve</option>
					<option value="F">Mike</option>
				</select></td>
				<td><input type="button"
					onClick="moveItems(this.form.SecondList,this.form.FirstList)"
					value="<<" style="height:50"> <input type="button"
					onClick="moveItems(this.form.FirstList,this.form.SecondList)"
					value=">>" style="height: 50"></td>
				<td align="left"><select name="SecondList" size="15" multiple
					id="SecondList" style="width: 250">
				</select></td>
			</tr>
			<tr align="center" valign="middle">
				<td colspan="3"><input type="submit" name="Submit"
					value="Submit" style="width: 250" class="button"
					onClick="selectAll(document.form1.SecondList);"></td>
			</tr>
		</table>

One thing to note here is that when the form submits all items on the second list are selected. This is the only way the items will be submitted as part of the form.

Thats all for now.

24
Dec
09

Managing JSF Checkboxes

This page describes the process of using checkboxes in your JSF enabled application to select all, select one or select multiple records in a dataTable.

In the examples below JavaScript is required. Submitting an http request to get the component state updated is not acceptable for a good user experience.

JSF Datatable

The following JSF code uses javascript defined below to check and un-check boxes.

<t:datatable renderedifempty="false" cellpadding="2" cellspacing="1" columnclasses="leftAlignCol" headerclass="list-header" id="dataTable1" rowclasses="list-row-even,list-row-odd" styleclass="dataTable" value="#{myBackingBean.queryResult}" var="currentRow" width="100%">
   <h:column>
       <t:selectbooleancheckbox forceid="true" id="chk" onclick="onChangeSelect(this);" value="#{myBackingBean.selected}"></t:selectbooleancheckbox>
       <f:facet name="header">
           <h:outputtext value="Selected"></h:outputtext>
       </f:facet>
  </h:column>
      :
    more columns
      :
</t:datatable>

JQuery

Using JQuery makes working with javascript easier. For example to implement the select all feature all you need to do is the following.

To get jquery just download it from their homepage. It is a small js file. Put it in the same directory as your html. Include this in the header of the page.

      <script src="jquery-1.3.2.min.js" type="text/javascript"></script>

include the following code that checks and unchecks the checkboxes.

   <a href="#" onclick="$('form :checkbox').attr('checked', 'checked');">Select All</a> 
   <a href="#" onclick="$('form :checkbox').attr('checked', '');">Un-Select All</a>

Thats all there is to it!

Doing it the OLD School way

If you guys insist in doing it the old way please read on. However take note that when you use pagination the second page will not work. The will only work when all the results are displayed on one page.

The old school method will only work if you use the tomahawk tag library and specify (forceId=”true” id=”chk” ). This allows us to write javascript that can check / uncheck the boxes referencing them via id. Otherwise JSF would have generated all sort of random id’s that would be difficult. JQuery does not have this problem since we are using the selector functionality. Even if the id’s were random JQuery would be able to check/uncheck the boxes.

Javascript Functions

To get all of this done we need the following javascript functions.

  1. Generic Function to get an element by its’ id.
  2. Generic function to  check to see if any item in the array has been checked
  3. Generic Function to call function 2 and enable a command button if it returns true
  4. Generic function to  hide or show an object using CSS.
  5. Select All Javascript

1. Gets an object based on its element id.

function getObj(objId, formId) {
        var fullId = objId;
        if (formId != null &amp;&amp; formId.length &gt; 0) {
            fullId = formId + ':' + objId;
        }
        //alert('getting object: ' + fullId);
        var elem = null;
        if (document.getElementById) {
            elem = document.getElementById(fullId);
        } else if (document.all) {
            elem = document.all[fullId];
        } else if (document.layers) {
            elem = document.layers[fullId];
        }
        return elem;
}

2. Generic function to check to see if any item in the array has been checked

/*
 * Browser-safe. Checks to see if an array of check boxes has any which are
 * checked. Boxes have ids like check[0], check[1], ... , check[n] where 'check'
 * is the base Id that has been assigned to the group.
 *
 * Param:               arrayId - String - id of element group to change
 * Returns:             boolean (true one or more checked) false (else)
 */
function checkBoxArrayHasChecked(arrayId) {
    for (i = 0; ; i++) {
        id = arrayId + '[' + i + ']';
        elem = getObj(id);
        if (elem == null) {
            break;
        } else if (elem.checked) {
           return true;
        }
    }
    return false;
}

3. Generic Function to call function 2 and enable a command button if it returns true

Custom function that will enable a component if at least one checkbox is checked.

var cmdBtnId = 'cmdbtn';
var formId = 'frm1';
var checkBoxArrayId = 'chk';
function onChangeSelect(checkbox) {
    // Render the transfer button if one or more checkboxes are selected
    hideOrShowObject(formId, cmdBtnId, checkBoxArrayHasChecked(checkBoxArrayId));
}

4. Generic function to check or un-check all elements in an array

/*
 * Browser-safe. Check or uncheck an array of checkboxes. Boxes have ids
 * like check[0], check[1], ... , check[n] where 'check' is the base Id that
 * has been assigned to the group.
 *
 * Param:               arrayId - String - id of element group to change
 * Param:               state - boolean - true (check all elements) false (uncheck all elements)
 * Returns:             nothing
 */
function checkBoxArraySet(arrayId, state) {
    for (i = 0; ; i++) {
        id = arrayId + '[' + i + ']';
        elem = getObj(id);
        if (elem == null) {
            break;
        } else {
            elem.checked = state;
        }
    }
}

5. Select All Javascript

The next javascript is implementation specific that will run when user clicks (select or un-select) all.

function setAll(state) {
    // Set the checkBox array on or off
    checkBoxArraySet(checkBoxArrayId, state);
}

Example

An example of a component that calls the above function.

<h:graphicimage onclick="setAll(true);" id="all" url="resources/add.gif" title="Select all records..."></h:graphicimage>

Another Example

		<a href="#" onclick="setAll(true);">
		     <h:outputtext value="Select all"></h:outputtext>
		</a><br>
		<a href="#" onclick="setAll(false);">
		     <h:outputtext value="de-Select all"></h:outputtext>
		</a>



Follow

Get every new post delivered to your Inbox.

Join 50 other followers