AJAX has become a most popular and widely used technology in modern web application. In some web application, around 95% of the server requests are AJAX requests. When you are developing your application, it is always a good practice finding more generic and reusable ways of implementing things. Sometime, we may call as inventing new patterns.
This tutorial explains a pattern of making your all AJAX requests through a single gateway. I am not explaining a technology feature, but this is programing technique.
What are the advantages?
Suppose you want to pass some common set of parameters with every AJAX request. This can be easily achieved by this technique.
If your application has an exception flow mechanism which brings exception messages happened in your DAO layer or service layer or even in the controller layer into the client view, those can be easily handled in central point with this technique.
If you want to show each operations status to the user, that can also be handled from a central point.
There may be more advantages.I will give you a sample implementation with jQuery. But this can be implemented with any Javascript library as you wish.
If you use jQuery, the following is the typical Javascript method using for AJAX request.
For my application, I have written a common central method to make every AJAX requests. This method works as a gateway which all AJAX requests are passing through this method. Any common functionality that we need to add before making the request as well as after making the request, can easily be added to this common method. The following code shows the common gateway method.
/**
* @author Semika siriwardana
*
*/
(function($) {
$.myObj = {
/**
* Central AJAX caller gateway.
*/
ajax: function(objConfig) {
$.ajax({
type: objConfig.type, //mandatory field
url: objConfig.url, //mandatory
headers: (objConfig.headers != undefined)?objConfig.headers:{},
async: (objConfig.async != undefined)?objConfig.async:true,
data : (objConfig.data != undefined)?objConfig.data:{},
dataType : (objConfig.dataType != undefined)?objConfig.dataType:'json',
cache: (objConfig.cache != undefined)?objConfig.cache:true,
contentType: (objConfig.contentType != undefined)?objConfig.contentType:'application/x-www-form-urlencoded',
success: function(data) {
//Handle success status and do more
objConfig.success(data); //Invoke success callback method.
},
error: function(data, textStatus, jqXHR) {
//Handle error status and do more
objConfig.error(data, textStatus, jqXHR)
}
});
}
}
}(jQuery));
As I mentioned before, this is a programing technique or a pattern. You know that '$.ajax()' is the method provided by jQuery for AJAX request. As you see above, I have written a wrapper method as '$.myObj.ajax(objConfig)'. Now onwords, when you want to make any AJAX requests, you are not directly invoking original '$.ajax()' method, but you invoke the wrapper method '$.myObj.ajax(objConfig)'.
Next, you will have a problem. What is this 'objConfig' parameter passing into '$.myObj.ajax()' method?. You will get the answer soon.
To make your AJAX request through our gateway method, you just need to change '$.ajax' to '$.myObj.ajax' from the above code.
Chatting is easy just like eating a piece of cake or drinking a hot coffee. Have you ever thought about developing a chat program by yourself?. You know that, it is not easy as chatting. But, if you are a developer and if you read to the end of this article, you may put a try to develop a chatting application by your self and allow your users to chat via your web application.
I had to implement a chatting application for my web application. As every one does, I started to search on internet. I found IRC. When I read and search more about IRC, I understood that finding a web base client for IRC was difficult.
I wanted to have more customizable web client which is working similar to Facebook. At last and luckily, I found CometD.
Finally, I was able to implement chatting application by using CometD and more customizable chat windows opening on the browser which is exactly similar to Facebook. This works almost all the modern browsers. This article explains step by step, How to implement chatting application from the scratch and also How to integrate chatting application to your existing Java base web application. Remember, Your web application should be a Java base one.
You need to download the cometD from their official web site. It has all the dependencies required to implement the chatting application except tow java script libraries. I have written two Javascript libraries, one to create dynamic chat windows like Facebook and other to handle CometD chatting functionality in generic way. If you can manage these stuff by your self, you don't need to use those tow Javascript libraries. Actually, CometD documentation provides good details. But, I go ahead with the tutorial by using those tow libraries. Any way, I recommend first use those tow libraries and then customize it as you need. I hope to share the sample application with you and you can deploy it in your localhost and test, how it works.
1.Adding required jar files.
If you use maven to build your project, add the following dependencies into your pom.xml file
If you are not using maven to build your project, just copy the following .jar files into /WEB-INF/lib folder from your CometD download bundle. You can find these .jar files from /cometd-demo/target/cometd-demo-2.5.0.war file.
bayeux-api-2.5.0.jar
cometd-java-annotations-2.5.0.jar
cometd-java-common-2.5.0.jar
cometd-java-server-2.5.0.jar
cometd-websocket-jetty-2.5.0.jar
javax.inject-1.jar
jetty-continuation-7.6.7.v20120910.jar
jetty-http-7.6.7.v20120910.jar
jetty-io-7.6.7.v20120910.jar
jetty-jmx-7.6.7.v20120910.jar
jetty-util-7.6.7.v20120910.jar
jetty-websocket-7.6.7.v20120910.jar
jsr250-api-1.0.jar
slf4j-api-1.6.6.jar
slf4j-simple-1.6.6.jar
2.Adding required Javascript files.
You need to link the following Javascript files.
cometd.js
AckExtension.js
ReloadExtension.js
jquery-1.8.2.js
jquery.cookie.js
jquery.cometd.js
jquery.cometd-reload.js
chat.window.js
comet.chat.js
The 'chat.window.js' and 'comet.chat.js' are my own tow Javascript libraries which does not come with CometD distribution. If you are totally following this tutorial, you have to link those tow libraries as well. Provided sample application has these tow Javascript libraries.
I think this section should be descriptive. If you allows your users to chat with other users, you need to show the list of online users in you web page, just like Facebook shows the online users inside the right side bar. For that, you can place a simple <span> or <div> tag inside your page. I have done it as follows.
<div id="members"></div>
All the online users will be displayed with in the above container. Once you click on a particular user name, it will open a new chat window similar to Facebook. For each pair of users, it will open a new chat window. To get this behaviour, you should use 'chat.window.js' which I mentioned before. Chatting in between particular pair of users will continue through a dedicated chat window.
Just after user is logging into your web application as usual way, we should subscribe that user to chat channels. You can do it using the following way.
Note that, I have passed the 'id' of online user list container as a configuration parameter. Then, user should be joined with channel as follows.You can call the bellow method with the username.
function join(userName){
$.cometChat.join(userName);
}
Since for each chat, there is a dedicated chat window just like Facebook, we should maintain global Javascript array to store those created chat window objects. You need to place the following Javascript code inside your page.
function getChatWindowByUserPair(loginUserName, peerUserName) {
var chatWindow;
for(var i = 0; i < chatWindowArray.length; i++) {
var windowInfo = chatWindowArray[i];
if (windowInfo.loginUserName == loginUserName && windowInfo.peerUserName == peerUserName) {
chatWindow = windowInfo.windowObj;
}
}
return chatWindow;
}
function createWindow(loginUserName, peerUserName) {
var chatWindow = getChatWindowByUserPair(loginUserName, peerUserName);
if (chatWindow == null) { //Not chat window created before for this user pair.
chatWindow = new ChatWindow(); //Create new chat window.
chatWindow.initWindow({
loginUserName:loginUserName,
peerUserName:peerUserName,
windowArray:chatWindowArray});
//collect all chat windows opended so far.
var chatWindowInfo = { peerUserName:peerUserName,
loginUserName:loginUserName,
windowObj:chatWindow
};
chatWindowArray.push(chatWindowInfo);
}
chatWindow.show();
return chatWindow;
}
As I mentioned above, declare following global Javascript variable.
var chatWindowArray = [];
var config = {
contextPath: '${pageContext.request.contextPath}'
};
Since I am using a JSP page, I have to get the context path via 'pageContext' variable. If you are using a HTML page, manage it by your self to declare 'config' Javascript global variable.
Now, you almost reached to last part of the tutorial.
5.How does the sample application works?
You can download the comet.war file and deploy it in your server. Point the browser to following URL.
http://localhost:8080/comet
This will bring you to a page which has a text field and button called "Join". Insert some user name as you wish and click on "Join" button. Then you will be forwarded to a another page which has list of online users. Your name is highlighted in red color. To chat in your local machine, You can open another browser (IE and FF) and join to the chat channel. The peer user displays in blue color in the online users list. Once you click on a peer user, it will open a new chat window so that You can chat with him. This functions very similar to Facebook chatting.
I have tested this chatting application in IE, FF and Crome and works fine. If you want any help of integrating this with your Java base web application, just send me a mail.
If you are using some container managed authentication mechanism like standard java form based authentication or spring security authentication mechanism, You are not really involving with user credential validation, like checking user name and password against the database, but the container is fully responsible for this. But what will happen if you want to pass some additional parameters with log-in details(username and password)?. The well known and most common scenario is passing "Keep me logged in" or "Remember me" check box value with your log-in details and doing some work with that while container is authenticating the user.
Recently, I had to implement the "Keep me logged in" function for one of my current project which are using spring 3 security as authentication mechanism. I am little new to spring 3 and it was challenging work for me of passing "Keep me logged in" check box status into spring's authentication provider class.
With this post, I will explain, How I achieved that.
The application uses 'AuthenticationProvider' class which extends from spring's 'AbstractUserDetailsAuthenticationProvider' and overrides 'retrieveUser' method which returns spring's UserDetails instance. Normally, authentication details are provided to 'retrieveUser' method via spring's 'WebAuthenticationDetails' instance.
Bellow shows the snip of code from 'retrieveUser' method of my authentication provider class.
@Override
protected User retrieveUser(String userName, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
OGGER.debug("Retrieve user : " + userName);
final String password = authentication.getCredentials().toString();
WebAuthenticationDetails webAuthenticationDetails = authentication.getDetails());
try {
User user = userBusiness.getUserByUsernameAndPassword(userName, password);
logger.debug("Remote address : " + webAuthenticationDetails.getRemoteAddress());
logger.debug("Session Id : " + webAuthenticationDetails.getSessionId());
//.................
return user;
} catch (Exception e) {
e.printStackTrace();
}
}
I wanted to get "Keep me logged in" check box value into 'retrieveUser' method. It was very obvious that I am not able to get the check box value with current situation. The 'WebAuthenticationDetails' provides some details like remote address, session id etc, But not our own additional details.
As the next step, I implemented my own custom class by extending 'WebAuthenticationDetails' and put 'rememberMe' as a bean property.That class shows bellow.
package com.blimp.webapp.security;
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
/**
* @author semika
*
*/
public class BlimpAuthenticationDetails extends WebAuthenticationDetails {
private static final long serialVersionUID = 2012033417540858020L;
private String rememberMe;
public String getRememberMe() {
return rememberMe;
}
//This constructor will be invoked by the filter
public BlimpAuthenticationDetails(HttpServletRequest request) {
super(request);
this.rememberMe = request.getParameter("rememberMe");
}
}
The next thing is, How we tell spring security engine to use my custom authentication detail class instead of using 'WebAuthenticationDetails' class when authenticating a user?.
For this one, we have to configure authentication processing filter in our spring security xml file. Some filtered contents from security XML file are shown bellow.
The 'daoAuthenticationProvider' is the instance of my 'AuthenticationProvider' class which extends 'AbstractUserDetailsAuthenticationProvider' and it has overridden 'retrieveUser' method. The my updated 'retrieveUser' method will be as follows.
@Override
protected User retrieveUser(String userName, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
OGGER.debug("Retrieve user : " + userName);
final String password = authentication.getCredentials().toString();
BlimpAuthenticationDetails webAuthenticationDetails = ((BlimpAuthenticationDetails) authentication.getDetails());
try {
User user = userBusiness.getUserByUsernameAndPassword(userName, password);
logger.debug("Remote address : " + webAuthenticationDetails.getRemoteAddress());
logger.debug("Session Id : " + webAuthenticationDetails.getSessionId());
logger.debug("Remember me : " + webAuthenticationDetails.getRememberMe());
//.................
return user;
} catch (Exception e) {
e.printStackTrace();
}
}
As you can see above, I can get the remember me check box value from 'retrieveUser' method.
I wanted to have AJAX file upload which supports multiple files uploading for my recent project. The Valums AJAX file up-loader gave me a very good experience and integrated that into my application. This file up-loader uses XHR for uploading files on the browsers which supports XMLHttpRequest level 2 and falls back to hidden iframe based upload in other browsers.
My application should run in FF, Chrome and IE. IE9 does not support XHR file uploads and even Valums GitHub has only provided a java example for XHR Level 2 supporting browsers which directly reads the input stream from the request.
For browsers which do not support XHR Level 2, up-loader send the file to server as 'multipart/form-data'. Therefor, we should write our up-loader java controller class so that it reads multipart form data when request coming from browsers which do not support XHR file upload (like IE9) and directly read the input stream for the requests coming from the browsers which support XHR file uploads (like FF3.6+, Safari4+, Chrome).
In both type of browsers, we should make sure that the response type as 'text/plain'.
My application is running with spring 3 MVC which persuaded me to do some work around when reading multipart form data from the request. In spring 3 MVC environment, 'HttpServletRequest' which has 'multipart/form-data' are wrapped into 'MultipartHttpServletRequest', not like in normal servlet environment.
Here, I am going to give you an example and basic java code which reads multipart form data with spring 3 MVC environment. If you are not using, spring 3 MVC and using some other controllers like pure servlets or struts 2 action class, you can directly read the multipart form data by using HttpServletRequest.
However, for Valums file up-loader, we should write our controller class in tow conditional way. One is for XHR file upload supporting browsers and one for 'multipart/form-data' based file uploading browsers.
@RequestMapping(value = "*ajax*", method = RequestMethod.POST)
public @ResponseBody
String uploadFile(HttpServletRequest request, HttpSession session, HttpServletResponse response, Principal principal, Model model, Locale locale) throws IOException, ServletException {
InputStream is = null;
String filename = null;
String result = null;
try {
if (isMultipartContent(request)) {
MultipartHttpServletRequest mrequest = (MultipartHttpServletRequest)request;
Map<String MultipartFile> fileMap = mrequest.getFileMap();
for (Map.Entry<String MultipartFile> entry : fileMap.entrySet()) {
MultipartFile mfile = entry.getValue();
is = mfile.getInputStream();
filename = mfile.getOriginalFilename();
break;
}
} else {
filename = request.getHeader("X-File-Name");
is = request.getInputStream();
}
result = "{success:true}";
} catch (Exception ex) {
ex.printStackTrace();
result = "{success:false}";
} finally {
try {
is.close();
} catch (IOException ignored) {}
}
return result;
}
private static final boolean isMultipartContent(HttpServletRequest request) {
String contentType = request.getContentType();
if (contentType == null) {
return false;
}
if (contentType.toLowerCase().startsWith("multipart/")) {
return true;
}
return false;
}
I hope this will help you. At the beginning, even I thought, Valums ajax file up-loader does not work in IE9. But it works fine in IE as well. If you need any help, feel free to put comment or send a mail to me.
I wanted to add internationalization and localization feature provided by spring 3 to one of my current project recently. I went through the spring documentation and then searched on internet to find some resources.
But I could not find a resource which was able to satisfy my client requirement. Most of the tutorials are like hello world application which gives basic understanding. Even spring documentation does not give in detailed explanation on integrating this feature to our own project. Expert developers can pick the stuff from spring documentation. But for others, have to put extra effort to make things up and running.
With this tutorial, I am going to explain very practical scenario that most of the clients are expecting.
The requirement
I am using spring security with my application. User should be able to select the language from the log-in page which was specified as 'login-page' of spring security XML file. I have provided links as "English","Chinese","German" and "Spanish" on top right corner of my log-in page to select the language. User can select the language and log in to the system by providing username and password. Then the whole application should be from the selected language. And also when selecting the language from the log-in page, the contents of the log-in page should also be changed.
Spring configurations
As the first step, I had to configure LocaleChangeInterceptor interceptor with in the dispatcher-servlet.xmlfile. This XML file name will change according to the name given to DispatcherServlet in web.xml file. I have given 'dispatcher' as the name for DispatcherServlet. So I should create 'dispatcher-servlet.xml' file under /WEB-INF folder. My application is running on Tomcat 7.
I could not make it working by following the way of declaring this interceptor as in the spring documentation. The request for changing the locale before log in(ie: from the login page) was not intercepted by the locale change interceptor. Therefore, I had to declare it as fallows.
The 'LocaleChangeInterceptor' will intercept the request asking for locale change and the corresponding locale code will be stored in the session with the help of
'SessionLocaleResolver'.
Next we will look at how to declare the 'SessionLocaleResolver' in the 'dispatcher-servlet.xml' file.
My application should support for 4 languages. So I added 4 property files into the 'resources' folder (ultimately all those property files should be in 'classes' folder) as follows.
messages_de.properties - German
messages_en.properties - English
messages_zh.properties - Chinese
messages_es.properties - Spanish
Note that, all the file names should start with the text which you specified as 'basenames' property of message resource bean.
The spring 3 security configurations were very important in this implementation. Keep in mind that, when you click any locale change link from the log-in page, you are not authenticated yet. But still that request should be intercepted by 'LocaleChangeInterceptor'. Otherwise, the language will not be changed as expected.
There fore, any anonymous user should be allowed to make locale change request and that request should go through the 'LocaleChangeInterceptor'.
Carefully look into my spring security configuration.
The login.jsp file is where user can log into the system by providing username and password and also that page has the corresponding links to change the locale. When user makes any request to a protected resource without authenticating, the user will be redirected to the login.jsp page. The above configuration says all the requests that are coming to the application should be from a authenticated user and also the user should be authorized except for the '/doChangeLocale**' request.
The intercept URL '/doChangeLocale**' is very important. Without that, the requests for changing locales are not intercepted by the locale change interceptor and finally locale will not change.
The followings are the locale change links that are placed in the login.jsp file.