Integrating JSR303 with Spring MVC 3 and ExtJS Forms

Previously in Spring MVC and ExtJS Forms I created a simple data entry form and there was no data validation implemented. Typically data validation occurs throughout application layers, from presentation to persistence layer. Here I will be using JSR-303 Bean Validation, where the data validation is defined in the domain model and can be used thoughout application layers. I will walk through how to integrate data validation into Spring MVC and will be using Hibernate Validator 4.x – which is the reference implementation for JSR-303 – to integrate into my previous example.

What is JSR-303?

“This JSR defines a metadata model and API for JavaBean validation. The default metadata source is annotations,
with the ability to override and extend the meta-data through the use of XML validation descriptors.
The validation API developed by this JSR is not intended for use in any one tier or programming model. It is specifically
not tied to either the web tier or the persistence tier, and is available for both server-side application programming,
as well as rich client Swing application developers.” – JSR-303 Specification

The data validation is achieved by defining constraints in the beans. Constraints are defined by a combination of constraint annotation and a list of constraint validation implementations. The constraint annotation can be applied at field-level, property-level (getter), class-level. Currently there are 22 built-in constraints for example @NotNull to check that the annotated value is not null or @Size(min=, max=) to check whether the annotated value lies between the specified range. You can also create your own custom constraint to meet your specific requirement. The concept here is similar to database constraints where the constraint can be applied to table column.

For now, I will walk through how to use the built-in constraints only. As usual, I will start with the configuration required to make the project work. Open the Maven pom.xml and add the following dependencies and repository:

<!-- JSR 303 with Hibernate Validator -->
<dependency>
	<groupId>javax.validation</groupId>
	<artifactId>validation-api</artifactId>
	<version>1.0.0.GA</version>
</dependency>
<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-validator</artifactId>
	<version>4.1.0.Final</version>
</dependency>
...
<!-- For Hibernate Validator Repository -->
<repository>
	<id>org.jboss.repository.release</id>
	<name>JBoss Maven Release Repository</name>
	<url>https://repository.jboss.org/nexus/content/repositories/releases</url>
	<snapshots><enabled>false</enabled></snapshots>
</repository>

In the example, the form is submitted to HomeController and the form is mapped to PersonalContact bean. I added 3 built-in constraints into the PersonalContact bean, @NotBlank, @Patter (using regular expression) and @Email. When message is not define in the constraint annotation, the validator will assign a default value. The second part of the code below shows the validation process. The HomeController is autowired to the Validator bean and the validation() is executed inside the add().

public class PersonalContact implements Serializable
{
	private static final long serialVersionUID = 1L;
	private Long id;

	@NotBlank(message="Please enter name")
	private String name;
               // using regular expression to define the constraint
	@Pattern(regexp="[0-9]{8}", message="Please enter 8 digit phone number")
	private String phone;

	@Email
	private String email;

	public PersonalContact() {}

	public PersonalContact(Long id, String name, String phone, String email) {
		super();
		this.id = id;
		this.name = name;
		this.phone = phone;
		this.email = email;
	}
	//getter & setter
}
@Controller
public class HomeController
{
	@Autowired
	private Validator validator;

	@RequestMapping(value="/", method=RequestMethod.GET)
	public String home() {
		return "home";
	}

	@RequestMapping(value="/load", method=RequestMethod.POST)
	public @ResponseBody Map<String, ? extends Object> load(PersonalContact input) {
	...
	}

	@RequestMapping(value="/add", method=RequestMethod.POST)
	public @ResponseBody Map<String, ? extends Object> add(PersonalContact input, HttpSession session) {
		Map<String, Object> data = new HashMap<String, Object>();

		Set<ConstraintViolation<PersonalContact>> failures = validator.validate(input);
		if (!failures.isEmpty()) {
			//structure the response for ExtJS Form.
			data.put("success",Boolean.FALSE);
			data.put("errors", validationMessages(failures));
			data.put("errorMessage", "Add Failed!");
		} else {
			session.setAttribute(input.getName(), input);
			data.put("success",Boolean.TRUE);
		}

		return data;
	}
	//iterate to retrieve validation errors and store it in HashMap
	private Map<String, String> validationMessages(Set<ConstraintViolation<PersonalContact>> failures) {
		Map<String, String> failureMessages = new HashMap<String, String>();
		for (ConstraintViolation<PersonalContact> failure : failures) {
			failureMessages.put(failure.getPropertyPath().toString(), failure.getMessage());
		}
		return failureMessages;
	}
}

Since I used ExtJS forms, I must return JSON response in a specific format and the errors are mapped to the field and displayed as quicktips.


The spring-mvc-forms project is updated and available for download at myGit. Hope you find it useful. Happy coding! 🙂

References

{
   success: true,
   errors: { field1: 'error for field 1',
                 field2: 'error for field 2',
   	   ...
   }
}
Advertisements

Customizing Spring Security Login using ExtJS

This tutorial will walk through the steps to customize Spring Security login page using Ext JS form. There are a few steps need to be done to customize the login form, because Ext JS form does not work in the same way as the standard HTML form. By default Ext Forms are submitted through Ajax and response packets are assumed to be JSON. I will be adding the customize login page into my previous example (in Spring 3 MVC and ExtJS Forms). I will walk through:
  1. configuration required to integrate with Spring Security 3 (3.0.5 to be exact),
  2. customize login page, and
  3. implement login handler to return JSON.
Step 1: Configuration required to integrate with Spring Security. If you are using the Spring MVC project template from STS, you will need to add dependencies to Spring Security by modifying the Maven pom.xml file. Add the following property and dependencies:
<properties>
	...
	<org.springframework.security-version>3.0.5.RELEASE</org.springframework.security-version>
	...
</properties>
<dependencies>
      ...
      <!-- Spring Security -->
      <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</artifactId>
            <version>${org.springframework.security-version}</version>
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
      </dependency>
    <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>${org.springframework.security-version}</version>
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
      </dependency>
    <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>${org.springframework.security-version}</version>
      </dependency>
    <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-taglibs</artifactId>
            <version>${org.springframework.security-version}</version>
      </dependency>
      ...
</dependencies>
Now, create security context file. Let’s name it applicationContext-security.xml. For this tutorial I used in memory user service and declare user id and password as part of the authentication manager. In the configuration, I declared the form login URL and the login handler. The login page is pointing to the login controller which forward to login.jsp. While login handler beans are used to return JSON to Ext JS form. The interceptor defined that every URL must be authenticated except for login and resources (images,css,javascript). Remember to set the access permission of login page to permit all to avoid endless loop. It is a good practice to default all URL to isAuthenticate() and only permit the necessary URL.
<http use-expressions="true">
    <intercept-url pattern="/resources/**" filters="none"/>
    <intercept-url pattern="/app/login.do" access="permitAll()" />
    <intercept-url pattern="/**" access="isAuthenticated()" />
      <form-login login-page="/app/login.do"
      			authentication-success-handler-ref="loginSuccessHandler"
      			authentication-failure-handler-ref="loginFailureHandler" />
      <logout invalidate-session="true" logout-success-url="/" logout-url="/j_spring_security_logout"/>
      <remember-me key="xaab.springmvclogin" />
      <session-management session-fixation-protection="newSession" >
          <concurrency-control max-sessions="1" error-if-maximum-exceeded="false"/>
      </session-management>
</http>
<!-- all password = password -->
<authentication-manager>
     <authentication-provider>
      	<password-encoder hash="md5" />
        <user-service>
            <user name="user1" password="5f4dcc3b5aa765d61d8327deb882cf99" authorities="ROLE_SUPERVISOR, ROLE_USER, ROLE_TELLER" />
            <user name="user2" password="5f4dcc3b5aa765d61d8327deb882cf99" authorities="ROLE_USER,ROLE_TELLER" />
            <user name="user3" password="5f4dcc3b5aa765d61d8327deb882cf99" authorities="ROLE_USER" />
            <user name="user4" password="5f4dcc3b5aa765d61d8327deb882cf99" authorities="ROLE_USER" />
         </user-service>
      </authentication-provider>
</authentication-manager>

<beans:bean id="loginSuccessHandler" class="org.xaab.springmvc.LoginSuccessHandler" />
<beans:bean id="loginFailureHandler" class="org.xaab.springmvc.LoginFailureHandler" />
In order to activate the above configuration, you need to include the security context into the context parameter and include the security filter in web.xml.
<!-- part of web.xml -->
<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>
		/WEB-INF/spring/root-context.xml
		/WEB-INF/spring/applicationContext-security.xml
	</param-value>
</context-param>

<filter>
      <filter-name>springSecurityFilterChain</filter-name>
      <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
Step 2: Customize login page. There are basic 3 elements in the default login page generated by Spring Security. They are:
  1. Form action: j_spring_security_check
  2. User name input: j_username
  3. Password input: j_password
Those are the form fields needed for the Ext login form. The UsernamePasswordAuthenticationFilter watches for a request to the virtual URL (/j_spring_security_check) used for form-based authentication, and attempts to authenticate the user. If necessary, You can customize it to use your own controller to authenticate. Take a look at the spring-mvc-login.js below. One thing to note is, this example redirect to a default URL upon successful login. It is done by modifying the window.location.
Ext.onReady(function(){
	Ext.QuickTips.init();

	var loginForm = new Ext.FormPanel({
		url: defLoginUrl,
		title: 'Login',
		renderTo: Ext.getBody(),
		frame: true,
		cls: 'my-form-class',
		width: 350,
		items: [{
			xtype: 'textfield',
			fieldLabel: 'Login',
			name: 'j_username'
	              },{
			xtype: 'textfield',
			inputType: 'password',
			fieldLabel: 'Password',
			name: 'j_password'
		}, {
			xtype: 'checkbox',
			fieldLabel: 'Remember Me?',
			name: '_spring_security_remember_me',
			checked: false
		}],
		buttons: [{
			id: 'lf.btn.login',
			text: 'Login',
			handler: function() {
				fnLoginForm(loginForm);
			}
		},{
			id: 'lf.btn.reset',
			text: 'Reset',
			handler: function() {
				fnResetForm(loginForm);
			}
		}]
	});

});
//Submit login and handler response
function fnLoginForm(theForm)
{
theForm.getForm().submit({
	success: function(form, action) {
		Ext.Msg.alert('Success', 'Login Successful!', function(btn, text) {
			if (btn == 'ok') {
				window.location = homeUrl;
			}
		});
	},
	failure: function(form, action) {
		Ext.Msg.alert('Warning', action.result.errorMessage);
	}
});
} //end fnLoginForm

function fnResetForm(theForm)
{
theForm.getForm().reset();
} //end fnResetForm
Step 3: Implement login handler to return JSON. Remember the authentication handlers declared in applicationContext-security.xml? now you will need implement it to handle succcess and failure case. For this purpose I used Jackson JSON processor and to include it add the dependency to pom.xml.
<!-- Jackson JSON Processor -->
<dependency>
	<groupId>org.codehaus.jackson</groupId>
	<artifactId>jackson-mapper-asl</artifactId>
	<version>1.7.4</version>
</dependency>
public class LoginSuccessHandler implements AuthenticationSuccessHandler
{
	public void onAuthenticationSuccess(HttpServletRequest request,
			HttpServletResponse response, Authentication auth) throws IOException,
			ServletException {

		ObjectMapper mapper = new ObjectMapper();
		LoginStatus status = new LoginStatus(true, auth.isAuthenticated(), auth.getName(), null);
		OutputStream out = response.getOutputStream();
		mapper.writeValue(out, status);
	}

}

public class LoginFailureHandler implements AuthenticationFailureHandler
{
	public void onAuthenticationFailure(HttpServletRequest request,
			HttpServletResponse response, AuthenticationException auth)
			throws IOException, ServletException {

		ObjectMapper mapper = new ObjectMapper();
		LoginStatus status = new LoginStatus(false, false, null, "Login failed. Try again.");
		OutputStream out = response.getOutputStream();
		mapper.writeValue(out, status);
	}

}
public class LoginStatus
{
  private final boolean success;
  private final boolean loggedIn;
  private final String username;
  private final String errorMessage;

  public LoginStatus(boolean success, boolean loggedIn, String username, String errorMessage) {
    this.success = success;
    this.loggedIn = loggedIn;
    this.username = username;
    this.errorMessage = errorMessage;
  }
}
Time to test drive the code. Lauch http://localhost:8080/spring-mvc-login/ in the browser and you will be redirected to the login page http://localhost:8080/spring-mvc-login/app/login.do.
Some of the things to improve on :
  • Use of SSL
  • Authenticate to LDAP
  • Securing “remember me” using Token.
Hope you find the tutorial useful. Happy coding! 🙂
Update: the source project is downloadable at myGit.

Spring 3 MVC and ExtJS Forms

In my previous post Beginning Spring MVC 3, I shared a simple tutorial to start developing Spring MVC 3. It covered the tools required and the steps to setup the project. This tutorial will take a step further and walk through how to work with forms – Ext JS form to be specific – for loading of data and submission of data for processing. I will be assuming that you have the tools and project setup.

Step 1. The model. Here I assumed a single bean will represent the form and data model (for data access/persistence). The attributes in the bean are mapped to the form fields.

public class PersonalContact implements Serializable
{
	private static final long serialVersionUID = 1L;
	private Long id;
	private String name;
	private String phone;
	private String email;

	public PersonalContact() {}

	public PersonalContact(Long id, String name, String phone, String email) {
		super();
		this.id = id;
		this.name = name;
		this.phone = phone;
		this.email = email;
	}

	//regenerate the getter & setter using STS.
}

Step 2. The controller. Using the default “HomeController”, I added 2 methods to handle the load and add request. Both methods have simple implementation to illustrate the interaction between the form and Spring MVC. There is no data access/persistence code implemented here.

@Controller
public class HomeController {

	private static final Logger logger = LoggerFactory.getLogger(HomeController.class);

	/**
	 * Simply selects the home view to render by returning its name.
	 */
	@RequestMapping(value="/", method=RequestMethod.GET)
	public String home() {
		logger.info("Welcome home!");
		return "home";
	}

	@RequestMapping(value="/load", method=RequestMethod.POST)
	public @ResponseBody Map<String, ? extends Object> load(PersonalContact input) {
		logger.info("Inside load");
		PersonalContact pc = new PersonalContact(1L, "Wowi", "89281932", "wo.wi@abcxyz.com");
		Map<String, Object> data = new HashMap<String, Object>();
		data.put("success",Boolean.TRUE);
		data.put("data", pc);

		return data;
	}

	@RequestMapping(value="/add", method=RequestMethod.POST)
	public @ResponseBody Map<String, ? extends Object> add(PersonalContact input, HttpSession session) {
		logger.info("Inside add");

		Map<String, Object> data = new HashMap<String, Object>();

		if (input.getName() == null) {
			data.put("success",Boolean.FALSE);
			data.put("errorMessage", "No Name?");
		} else if (session.getAttribute(input.getName()) != null) {
			data.put("success",Boolean.FALSE);
			data.put("errorMessage", "There is an existing data, unable to add. Please enter a different name");
		} else {
			session.setAttribute(input.getName(), input);
			data.put("success",Boolean.TRUE);
		}

		return data;
	}
}

Step 3. The view. Similar to the controller, I built on the default home.jsp. As you can see there isn’t much code in the JSP. The core UI codes are in the JavaScript (spring-mvc-forms.js). By default the response assumed to be in JSON. Do remember to add the Jackson into the pom.xml and set the header to “Accept” application/json.

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
	<title>Home</title>
	<link href="<c:url value="/resources/lib/ext-3.3.1/resources/css/ext-all.css" />" rel="stylesheet" type="text/css" />
	<script type="text/javascript" src="<c:url value="/resources/lib/ext-3.3.1/adapter/ext/ext-base.js" />"></script>
	<script type="text/javascript" src="<c:url value="/resources/lib/ext-3.3.1/ext-all.js" />"></script>
	<script type="text/javascript">
		if (Ext.BLANK_IMAGE_URL.substr(0, 5) != 'data:') {
			Ext.BLANK_IMAGE_URL = '<c:url value="/resources/lib/ext-3.3.1/resources/images/default/s.gif" />';
		}
		var loadUrl = '<c:url value="/load" />';
		var addUrl = '<c:url value="/add" />';
	</script>
	<script type="text/javascript" src="<c:url value="/resources/js/spring-mvc-forms.js" />"></script>
	<style type="text/css">
		body {
		  	font: normal 12px helvetica,arial,verdana,tahoma,sans-serif;
		}
		.my-form-class {
			margin:  20px 30px;
		}
	</style>
</head>
<body>
</body>
</html>
Ext.onReady(function(){
	Ext.QuickTips.init();

	var mf = new Ext.FormPanel({
			url: addUrl,
			renderTo: Ext.getBody(),
			frame: true,
			cls: 'my-form-class',
			width: 350,
			items: [{
					xtype: 'textfield',
					fieldLabel: 'Name',
					name: 'name'
			},{
					xtype: 'textfield',
					fieldLabel: 'Phone No.',
					name: 'phone'
			},{
					xtype: 'textfield',
					fieldLabel: 'EMail',
					name: 'email'
			}],
			buttons: [{
					id: 'mf.btn.load',
					text: 'Load',
					handler: function() {
						fnLoadForm(mf);
					}
				},{
					id: 'mf.btn.add',
					text: 'Add',
					disabled: true,
					handler: function() {
						fnUpdateForm(mf);
					}
			},{
					id: 'mf.btn.reset',
					text: 'Reset',
					disabled: true,
					handler: function() {
						fnResetForm(mf);
					}
			}]
	});

});

function fnLoadForm(theForm)
{
	//for the purpose of this tutorial, load 1 record.
	theForm.getForm().load({
		url: loadUrl,
		headers: {Accept: 'application/json, text/javascript, */*; q=0.01'},
    waitMsg: 'loading...',
		params : {
			id: 1
		},
		success: function(form, action) {
			Ext.getCmp('mf.btn.add').setDisabled(false);
			Ext.getCmp('mf.btn.reset').setDisabled(false);
			Ext.getCmp('mf.btn.load').setDisabled(true);
		},
		failure: function(form, action) {
			Ext.Msg.alert('Warning', 'Error Unable to Load Form Data.');
		}
	});
} //end fnLoadForm
function fnUpdateForm(theForm)
{
	theForm.getForm().submit({
		success: function(form, action) {
			Ext.Msg.alert('Success', 'Data is stored in session.');
			form.reset();
		},
		failure: function(form, action) {
			Ext.Msg.alert('Warning', action.result.errorMessage);
		}
	});
} //end fnUpdateForm
function fnResetForm(theForm)
{
	theForm.getForm().reset();
	Ext.getCmp('mf.btn.add').setDisabled(true);
	Ext.getCmp('mf.btn.reset').setDisabled(true);
} //end fnResetForm

Now that all the components are ready, launch the example and to begin with click “load” button. The form will be pre-populated with example data. To try the form submission, click “add”. Hope you find the tutorial useful. Happy coding! 🙂

Update: the source project is downloadable at myGit.

Beginning Spring MVC 3

It’s been awhile since I used Spring framework. It has evolved and gotten simpler & easier to work with. This tutorial will walk through how to setup a simple Spring MVC project and to return JSON object to client browser. In this tutorial I will be using SpringSource Tool Suite, its project template helps to reduce the time to get a Spring MVC project up and running.

In order to start the tutorial, you will need

  1. JDK installed. SpringSource Tool Suite requires full JDK, and
  2. SpringSource Tool Suite (STS). The installer includes the IDE and tc Server Developer Edition.

Let’s start with creating the Spring MVC project in STS.

Step 1: Create a new project:  File -> New -> Spring Template Project. You will be prompted to download the template, go ahead and download.

Step 2: Define project and top-level package name, then click “Finish”

Step 3: After the project created you will see the file structure and default controller (HomeController). The dependencies are defined in pom.xml and you will need to add Jackson JSON Processor to complete the tutorial. Without the library you will get HTTP 406 when getting JSON object. Add the following to pom.xml.

<!-- Jackson JSON Processor -->
<dependency>
 <groupId>org.codehaus.jackson</groupId>
 <artifactId>jackson-mapper-asl</artifactId>
 <version>1.6.4</version>
</dependency>


Step 4: Deploy the project into SpringSource tc Server. It cannot get easier then this.

Step 5: Deploy the project then start the server.


Step 6: Once the server is ready. Test you configuration by opening http://localhost:8080/spring-mvc-basic/ in your browser. That is all for the setup, now we move on to add a simple function to get JSON data and to get request information.

Step 7: Open HomeController and add 2 methods. The displayRequest() is a simple GET method which take an input parameter “input”, servlet request and request header. The content will be passed back to the view, in this case display.jsp. The 2nd method is getJsonData(), a simple method that return JSON object.

	@RequestMapping(value="/display", method=RequestMethod.GET)
	public String displayRequest(HttpServletRequest request,
		@RequestHeader(value="Accept") String accept,
		@RequestHeader("Accept-Encoding") String encoding,
		@RequestHeader("Accept-Charset") String charset,
                @RequestParam("input") String input,
                Model model)  {
	  logger.info("Calling displayRequest");
	  model.addAttribute("RemoteAddress", request.getRemoteAddr());
	  model.addAttribute("HeaderAccept", accept);
	  model.addAttribute("HeaderAcceptEncoding", encoding);
	  model.addAttribute("HeaderAcceptCharset", charset);
	  model.addAttribute("Output", input);
	  return "display";
	}

	@RequestMapping(value="/extractJson", method=RequestMethod.GET)
	public @ResponseBody Map<String,String> getJsonData(@RequestParam("input") String input)  {
	  logger.info("Calling extractJson");
	  person.put("name", "Simba");
	  person.put("website", "http://www.tanbh.net");
	  person.put("output", input);
	  return person;
	}

Step 8: Let’s work on the views now, by adding the link to call the controller “/display” and jQuery code to get JSON.

<!-- home.jsp -->
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
	<title>Home</title>
	<script type="text/javascript" src="http://code.jquery.com/jquery-1.5.1.min.js"></script>
</head>
<body>
<h1>Hello Spring MVC 3!</h1>
<a href="<c:url value="/display?input=HelloWorldEcho" />">Click to Get Request Info</a>
<br/><br/>
<div id="result"></div>
</body>
</html>
<script type="text/javascript">
$(document).ready(function() {

	$.getJSON('<c:url value="extractJson?input=HelloWorldEcho" />', function(data) {
	    $('#result').append('Result from ajax/json<br/>');
	    $('#result').append('Name: ' + data.name);
	    $('#result').append('<br/>');
	    $('#result').append('Website: ' + data.website);
	    $('#result').append('<br/>');
	    $('#result').append('Input Parameter: ' + data.output);
	});
});
</script>
<!-- display.jsp -->
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<strong><c:out value="Remote Address: ${RemoteAddress}"></c:out></strong><br/>
<strong><c:out value="Accept ${HeaderAccept}"></c:out></strong><br/>
<strong><c:out value="Accept-Encoding: ${HeaderAcceptEncoding}"></c:out></strong><br/>
<strong><c:out value="Accept-Charset: ${HeaderAcceptCharset}"></c:out></strong><br/><br/>
<strong><c:out value="Input Parameter: ${Output}"></c:out></strong><br/>

<br/>
<br/>
<br/>
<a href="<c:url value="/" />">Home</a>

</body>
</html>

Now save all the file and sit back and wait for tc Server to reload. Once reloaded, launch trigger http://localhost:8080/spring-mvc-basic/. I also observed, I need to restart the tc Server after compilation error in the controller. Anyway, you should see your result:

Update: the source is downloadable at myGit.

ExtJS Google Maps Panel

I was browsing the Ext JS (3.3.x) example and saw the Google Maps Panel which was written based on Google Maps API v2. I thought it might be a good idea to migrate it to use v3, since v2 has been officially deprecated for sometime. Beside migrating the code, I modified the input parameter to accept MapOptions and also added 2 new features:

  • option to show InfoWindow at the center & other markers.
  • option to draw circle overlay at the center marker.

The full source code and example is available at myGit. I did not include Ext JS 3.3.1 into the repository.

Ext.ux.GoogleMapPanel = Ext.extend(Ext.Panel, {
		...
    addMarker : function(point, marker, clear, center, listeners, infoWindowOptions){
        if (clear === true){
            this.getMap().clearOverlays();
        }
        if (center === true) {
            this.getMap().setCenter(point);
        }

				var mark = new google.maps.Marker({
				       map: this.getMap(),
				       position: point,
				       title: marker.title
				});

				var infoWindow = null;
				if (infoWindowOptions != null) {
				   infoWindow = new google.maps.InfoWindow(infoWindowOptions);
				   	google.maps.event.addListener(mark, 'click', function() {
			  			infoWindow.open(this.getMap(),mark);
					});
				}
        if (typeof listeners === 'object'){
            for (evt in listeners) {
                google.maps.event.addListener(mark, evt, listeners[evt]);
            }
        }
        return mark;
    },
    drawCircle: function(circleOptions) {
	      this.circleOverlays = new google.maps.Circle(circleOptions);
        this.circleOverlays.setCenter(this.getCenter());
        this.circleOverlays.setMap(this.getMap());
     }
     ...
});

Ext.reg('gmappanel', Ext.ux.GoogleMapPanel);     

Time to Say Goodbye to IE6

I am writing this in support of the Microsoft IE6 Countdown campaign. The campaign website was launched last Fri, 4th Mar 11 and to my surprise it is from Microsoft. It’s been ~10 years since it first release in August 01. IE6 is well-known for its compatibility issue and web developers often need to write IE specific fix. The good news is the number of users is 9% lower than previous year as shown in the “map” below. The biggest usage of IE6 are in China and South Korea. With such a large population using it, I wonder how long more it will take to reach < 1%.

One other thing to notice in the website is that the link “Want to learn about Internet Explorer 9?” is unnoticeable at the bottom right corner of the page. Could it be due to large number of IE6 users are still using Windows XP and IE9 is not compatible with XP? or Could be it be due to the cost of migration for business is too high?

Anyway, support the campaign by adding the banner provided in the site or tweet about it. 🙂 http://www.ie6countdown.com

 

IE6 Countdown Map - ie6countdown.com

 

IE6 Countdown Detail Breakdown - ie6countdown.com