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.




Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Join 75 other followers

April 2017
S M T W T F S
« Mar    
 1
2345678
9101112131415
16171819202122
23242526272829
30  

Blog Stats

  • 806,836 hits