2 jsp pages,
3 servlet,
1 css file
1 js file
and it use servlet3 annotation to do config so no web.xml
described as follows:
index.jsp
<%@ page isErrorPage="true" language="java"
contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page isELIgnored ="false" %>
<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=UTF-8"/>
<title>Login page</title>
</head>
<body>
<form action="login.go" method="post">
<span>Type a name then press login to enter chat room</span>
<input id="userId" type="text" value="Your ID" name="uid" />
<input type="submit" value="login"/>
</form>
</body>
</html>
only a form, post to login.go with the user id
Login.java
package test.jsp.simplechat;
import java.io.IOException;
import java.util.*;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(name="LoginServlet", urlPatterns={"/login.go"},
loadOnStartup=1)
public class Login extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String uid = new String(req.getParameter("uid").getBytes("ISO-8859-1"), "UTF-8");
String newUid = uid;
int i = 2;
Map chat = Chat.getChatMap();
synchronized (chat) {
// prevent uid conflict
if ("you".equalsIgnoreCase(newUid))
newUid = uid + i++;
while (chat.containsKey(newUid))
newUid = uid + i++;
uid = newUid;
chat.put(uid, new ArrayList());
}
req.getSession().setAttribute("UID", uid);
resp.sendRedirect("chat.jsp");
}
}
the login servlet, check the user id here, if the user id is 'you' then append a number to it, because 'you' is a key word for display self message. Also append number to solve any id conflict.
Note the
String uid = new String(req.getParameter("uid").getBytes("ISO-8859-1"), "UTF-8");
is required or the utf-8 char will not stored to map correctly.
Finally store uid in session then redirect to chat.jsp
chat.jsp
<%@ page isErrorPage="true" language="java"
contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ page isELIgnored ="false" %>
<!-- Redirect to index.jsp if no UID -->
<c:if test="${UID == null}">
<c:redirect url="index.jsp" />
</c:if>
<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=UTF-8"/>
<title>Login page</title>
<link href="css/chat.css" rel="stylesheet" type="text/css">
<script type="text/javascript" src="js/chat.js"></script>
</head>
<body>
<form action="logout.go" method="post">
<div>This is chat page</div>
<div>Type message then press ENTER key to send message</div>
<div>Click logout to return the login page</div>
<div>Your name: <span id="uid">${UID}</span></div>
<div id="content" class="content"></div>
<div>
<!-- listen to keyup to send message if enter pressed -->
<textarea class="msg-input" onkeyup="chat.dokeyup(event);">input text here</textarea>
</div>
<input type="submit" value="logout" />
</form>
</body>
</html>
First check UID and redirect to 'login.jsp' if UID is not in session.
link to style and js file, there is a timer will start while load js file.
listen to keyup event of input area to send content if enter pressed,
logout if logout button clicked.
chat.js
// init
window.chat = {};
// post to send message to chat.do
chat.sendMsg = function (msg) {
var request;
msg = msg.replace(/&/g, '&') // encode to prevent XSS
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/\n/g, '<br />'); // replace textarea newline to line break tag
alert(msg);
if (request = this.getXmlHttpRequest()) {
request.open('POST', 'chat.do?action=send&msg='+msg+'&time='+new Date().getTime());
request.send(null);
chat.updateContent('<div>You said: '+msg+'</div>');
}
};
// post 'get' action to chat.do to require new message if any
chat.startListen = function () {
if (!chat.listen)
chat.listen = setInterval (function () {
var request;
if (request = chat.getXmlHttpRequest()) {
request.open('POST', 'chat.do?action=get&time='+new Date().getTime());
request.send(null);
request.onreadystatechange = function() {
if(request.readyState === 4) {
if(request.status === 200) {
var json = request.responseText;
// has new message
if (json && json.length) {
// parse to array
var obj = eval('(' + json + ')');
var msg = '';
for (var i = 0; i < obj.length; i++) {
msg += '<div>'+obj[i]+'</div>';
}
chat.updateContent(msg);
}
} else if(request.status === 400 || request.status === 500)
document.location.href = 'index.jsp';
}
};
}
}, 3000);
};
chat.updateContent = function (msg) {
var content = document.getElementById('content'),
atBottom = (content.scrollTop + content.offsetHeight) >= content.scrollHeight;
content.innerHTML += msg;
// only scroll to bottom if it is at bottom before msg added
if (atBottom)
content.scrollTop = content.scrollHeight;
};
chat.dokeyup = function (event) {
if (!event) // IE will not pass event
event = window.event;
if (event.keyCode == 13 && !event.shiftKey) { // ENTER pressed
var target = (event.currentTarget) ? event.currentTarget : event.srcElement,
value = target.value;
// make sure not only space char
if (value && value.replace(/^\s\s*/, '').replace(/\s\s*$/, '').length > 0) {
this.sendMsg(target.value);
target.value = '';
}
}
};
// get the XmlHttpRequest object
chat.getXmlHttpRequest = function () {
if (window.XMLHttpRequest
&& (window.location.protocol !== 'file:'
|| !window.ActiveXObject))
return new XMLHttpRequest();
try {
return new ActiveXObject('Microsoft.XMLHTTP');
} catch(e) {
throw new Error('XMLHttpRequest not supported');
}
};
onload = function () {
chat.startListen();
};
This file do two things,
It starts a timer after page loaded by chat.startListen();, it will periodically send an ajax request to server to get the latest message if any then put them into content div and scroll content to bottom as need.
The keyup event of input area will trigger the chat.dokeyup, then call chat.sendMsg if enter pressed and input area is not empty.
Chat.java
package test.jsp.simplechat;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sf.json.JSONArray;
@WebServlet(name="ChatServlet", urlPatterns={"/chat.do"},
loadOnStartup=1)
public class Chat extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 113880057049845876L;
// message map, mapping user UID with a message list
private static Map<String, List<String>> _chat = new HashMap<String, List<String>>();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
String action = req.getParameter("action");
// send message
if ("send".equals(action)) {
// get param with UTF-8 enconding
String msg = new String(req.getParameter("msg").getBytes("ISO-8859-1"), "UTF-8");
String uid = (String)req.getSession().getAttribute("UID");
for (String s : _chat.keySet()) {
if (!s.equals(uid)) {
synchronized (_chat.get(s)) {
// put message to any other user's msg list
_chat.get(s).add(uid+" said: "+msg);
}
}
}
} else if ("get".equals(action)) { // get message
String uid = (String)req.getSession().getAttribute("UID");
if (uid == null)
resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
List<String> l = _chat.get(uid);
synchronized (l) {
if (l.size() > 0) {
// for UTF-8 chars
resp.setCharacterEncoding("UTF-8");
PrintWriter out = resp.getWriter();
JSONArray jsna = new JSONArray();
// add all msg to json array and clear list
while (l.size() > 0)
jsna.add(l.remove(0));
out.println(jsna);
out.close();
}
}
}
}
public static Map<String, List<String>> getChatMap () {
return _chat;
}
}
This class hold a map which mapping the user id with a message list,
when a user request send the message, it put the message to all other user's message list.
when a user request get message, it put all message from that user's message list into a json array and write it to response writer.
chat.css
.content {
width: 600px;
height: 400px;
overflow: auto;
border: 1px solid #991111;
}
.msg-input {
width: 600px;
border: 1px solid #119911;
}
simply style the content and message input area.
Logout.java
package test.jsp.simplechat;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(name="Logout", urlPatterns={"/logout.go"},
loadOnStartup=1)
// practice: invalidate session
public class Logout extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = -6175876557872938832L;
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.getSession().invalidate();
resp.sendRedirect("index.jsp");
}
}
Simply invalidate the session and redirect to index.jsp
Download:
Download full project from github:
https://github.com/benbai123/JSP_Servlet_Practice/tree/master/Practice/JSPChat
Dependency:
The required jar files:
commons-beanutils-1.8.3.jar
commons-collections-3.2.1.jar
commons-lang-2.5.jar
commons-logging-1.1.1.jar
ezmorph-1.0.6.jar
json-lib-2.4-jdk15.jar // these six are for json
jstl-1.2.jar // this one is for JSTL
Hi, thanks for the tutorial, but can you tell some other kinf of communication between the servlet and the .js file. I mean can we use some kind of our own object type instead of JSONArray?
ReplyDeleteYou can create js object based on java object in your way as needed.
DeleteThe steps are as below:
1. Build js string based on the specific object at server side.
2. Response that string to client side.
3. Evaluate that string to build js object as needed at client side.
Infact your code does'n work properly with special symbols like <>#, expecially with %, then throws exception "Character decoding failed. Parameter [msg] with value [%%3Cbr%20/%3E] has been ignored." Inside the Map we receive null. How can we overcome this thing?
ReplyDeleteRegarding < and >, I believe they should work well since I encoded them in chat.js line 9 and 10. Regarding # and %, you can also encode them (i.e., add .replace(/%/g, '%').replace(/#/g, '#')) as needed, please refer to HTML ASCII Reference for more information
Deletehttp://www.w3schools.com/tags/ref_ascii.asp
If there are still any issues you may need to check the character encoding of your system or web container.
And yes, this is a simple practice, there should lots of places have to be improved.
hi,
ReplyDeleteThis is nice and simple code.
I am trying your code and it is working for login page. after clicking on login that page gives me 404 erroe. How to over come this.
Can i have to add any class in web.xml?
This project works with servlet 3, you can run this project with the jars listed in Dependency sub section in Tomcat 7 with servlet 3.0 settings.
DeleteFor servlet 2.4, please refer to the web.xml
Deletehttps://gist.github.com/benbai123/5346429#file-web-xml
tested with tomcat 6
Thanks It is working... but i am facing some error on Chat. java page. Error is "The method add(String) is undefined for the type JSONArray"
DeleteDid you use the required jars listed in Dependency section? There are a lot of JSON implementation, they might have different API.
DeleteThanks for your reply... i have check all required jars listed in Dependency section.. but no luck.. Getting same error.
ReplyDeleteI'm not sure what is your problem, maybe you can try the war file directly
Deletehttp://dl.dropbox.com/u/90546423/JSPChat24.war
Hello, I have a problem. I have opened it in two browsers to try it and when I write in one of them i get a logout in the other window and I do not know what it is happening ¿any idea?
ReplyDeleteYou can check whether this line below within chat.js is executed:
Deletedocument.location.href = 'index.jsp';
if so this means the ajax request cannot connect to your page
I have the same problem.What should i do to solve this??
Delete
Deleteit should be redirected to index.jsp by chat.js Line 44
https://goo.gl/tJcjYd
probably because server response a SC_BAD_REQUEST error
( Chat.java Line 45 https://goo.gl/21Geuo )
or a HTTP Code 400 Error caused by
the Web Server cannot manage the resource properly
the sample just tested with Tomcat 7
To check the status,
you can check the status code with
alert(request.status)
or
console.log(request.status)
(if pressed F12 to open the console)
To check the get action,
you can output uid before Chat.java Line 44 by
System.out.println(uid);
You can also try to debug with debugger,
some tutorial:
Java Debugging with Eclipse - Tutorial
http://goo.gl/kaNlJ9
Using the Console
https://goo.gl/XKVdGR
this is only shows my message not other messages
ReplyDeleteThis may related to your server/JEE version or some other reason, the test environment of this article is Tomcat7/JDK6 (if memory served :p)
Deletewhat is the mean of chat.do logout.go
ReplyDeletein .jsp, it means the form will post to the url chat.go,
Deletein .java, it means the servlet will process request that post to chat.go
please submit the demo of your project
ReplyDeleteSorry but I cant understand what you mean.
DeleteYour blog has excellent information On JSP.
ReplyDeleteFrom
www.javatportal.com
I got a index page ,login redirects to the chat page ,then what about messages they are not displayed ??
ReplyDeleteWith this sample they are just skipped, you will need to use a database if you want new logged in user can see all old message.
DeleteThis comment has been removed by the author.
ReplyDeleteI can only see the messages which I type.But I am not getting any messages from the server .
ReplyDeleteTo see messages from multiople users, you need to login with another browser (for different session) and type messages in both browser.
Deletewill this work like a chat box ?just like any other chat engine ?
Deleteguess not, basically I do not know what other chat engine did, this is just a simple practice with respect to share message between multiple users.
DeleteI send a message and someone should be able to reply to the message does it wok like that ??
ReplyDelete
DeleteSimilar to that but not the same,
chat.js Line 15 it sends message to server
https://goo.gl/aKvfwu
then Chat.java Line 38 store the message to current users' message List
https://goo.gl/lOfZgN
Note that the message will be sent to all users
after that when a user POST a request that action=‘get’
( chat.js Line 27 https://goo.gl/E8ulE4 )
the server will response all unread message
( Chat.java Line 55 https://goo.gl/s52FtV )
to send message to specific user,
you will need to add an input field for target user id,
send this info to server with message,
and put the message to only the list of target user.
I will try this but will it work like a messenger where 2 people can have a conversation??
Deletenope, it will similar with the 'send private message' feature in a public chat room.
Deleteto work like a private conversation you will need one more options (probably a checkbox or completely different page) to let user refuse all messages except the message from specific id
thank you for this application, i test the app with tow browsers, when start typing after Enter the other browser redirect to index, I traced the error the request.status is 500, how can i fix the error
ReplyDeleteRegarding low browsers, do you mean IE7 or below? Are there any error in the console?
Delete