I am implementing a chat room with webscoket by Spring boot
This is the code
ChatMessage.Class
public class ChatMessage {
private MessageType type;
private String content;
private String sender;
public enum MessageType {
CHAT,
JOIN,
LEAVE
}
public MessageType getType() {
return type;
}
public void setType(MessageType type) {
this.type = type;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getSender() {
return sender;
}
public void setSender(String sender) {
this.sender = sender;
}
}
My WebSocketConfig.Class
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app");
registry.enableSimpleBroker("/topic");
}
}
WebSocketListener.Class
#Component
public class WebSocketEventListener {
#Autowired
private SimpMessageSendingOperations messagingTemplate;
#EventListener
public void handleWebSocketConnectListener(SessionConnectedEvent event) {
System.out.println("Received a new web socket connection");
}
#EventListener
public void handleWebSocketDisconnectListener(SessionDisconnectEvent event) {
StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(event.getMessage());
String username = (String) headerAccessor.getSessionAttributes().get("username");
if (username != null) {
ChatMessage chatMessage = new ChatMessage();
chatMessage.setType(ChatMessage.MessageType.LEAVE);
chatMessage.setSender(username);
messagingTemplate.convertAndSend("/topic/public", chatMessage);
}
}
}
ChatController:
#Controller
public class ChatController {
#Autowired
private SimpMessagingTemplate simpMessagingTemplate;
#MessageMapping("/chat.sendMessage/{room}")
public void sendMessage(#Payload ChatMessage chatMessage, #DestinationVariable String room) {
simpMessagingTemplate.convertAndSend("/topic/public/"+room, chatMessage);
}
#MessageMapping("/chat.addUser/{room}")
public void addUser(#Payload ChatMessage chatMessage,
SimpMessageHeaderAccessor headerAccessor, #DestinationVariable String room) {
// Add username in web socket session
headerAccessor.getSessionAttributes().put("username", chatMessage.getSender());
simpMessagingTemplate.convertAndSend("/topic/public/"+room, chatMessage);
}
}
My Index.html:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
<title>Spring Boot WebSocket Chat Application</title>
<link rel="stylesheet" href="/css/main.css" />
</head>
<body>
<noscript>
<h2>Sorry! Your browser doesn't support Javascript</h2>
</noscript>
<div id="username-page">
<div class="username-page-container">
<h1 class="title">Type your username</h1>
<form id="usernameForm" name="usernameForm">
<div class="form-group">
<input type="text" id="name" placeholder="Username" autocomplete="off" class="form-control" />
</div>
<div class="form-group">
<input type="text" id="room_num" placeholder="RoomNum" autocomplete="off" class="form-control"/>
</div>
<div class="form-group">
<button type="submit" class="accent username-submit">Start Chatting</button>
</div>
</form>
</div>
</div>
<div id="chat-page" class="hidden">
<div class="chat-container">
<div class="chat-header">
<h2>Spring WebSocket Chat Demo</h2>
</div>
<div class="connecting">
Connecting...
</div>
<ul id="messageArea">
</ul>
<form id="messageForm" name="messageForm">
<div class="form-group">
<div class="input-group clearfix">
<input type="text" id="message" placeholder="Type a message..." autocomplete="off" class="form-control"/>
<button type="submit" class="primary">Send</button>
</div>
</div>
</form>
</div>
</div>
<script src="https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js"></script>
<script src="https://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.js"></script>
<script src="/js/main.js"></script>
</body>
</html>
main.js:
'use strict';
var usernamePage = document.querySelector('#username-page');
var chatPage = document.querySelector('#chat-page');
var usernameForm = document.querySelector('#usernameForm');
var messageForm = document.querySelector('#messageForm');
var messageInput = document.querySelector('#message');
var messageArea = document.querySelector('#messageArea');
var connectingElement = document.querySelector('.connecting');
var stompClient = null;
var username = null;
var room_num = null;
var colors = [
'#2196F3', '#32c787', '#00BCD4', '#ff5652',
'#ffc107', '#ff85af', '#FF9800', '#39bbb0'
];
function connect(event) {
username = document.querySelector('#name').value.trim();
room_num = document.querySelector('#room_num').value.trim();
if(username) {
usernamePage.classList.add('hidden');
chatPage.classList.remove('hidden');
var socket = new SockJS('/ws');
stompClient = Stomp.over(socket);
stompClient.connect({}, onConnected, onError);
}
event.preventDefault();
}
function onConnected() {
// Subscribe to the Public Topic
stompClient.subscribe('/topic/public/'+room_num, onMessageReceived);
// Tell your username to the server
stompClient.send("/app/chat.addUser/"+room_num,
{},
JSON.stringify({sender: username, type: 'JOIN'})
);
connectingElement.classList.add('hidden');
}
function onError(error) {
connectingElement.textContent = 'Could not connect to WebSocket server. Please refresh this page to try again!';
connectingElement.style.color = 'red';
}
function sendMessage(event) {
var messageContent = messageInput.value.trim();
if(messageContent && stompClient) {
var chatMessage = {
sender: username,
content: messageInput.value,
type: 'CHAT'
};
stompClient.send("/app/chat.sendMessage/"+room_num, {}, JSON.stringify(chatMessage));
messageInput.value = '';
}
event.preventDefault();
}
function onMessageReceived(payload) {
var message = JSON.parse(payload.body);
var messageElement = document.createElement('li');
if(message.type === 'JOIN') {
messageElement.classList.add('event-message');
message.content = message.sender + ' joined!';
} else if (message.type === 'LEAVE') {
messageElement.classList.add('event-message');
message.content = message.sender + ' left!';
} else {
messageElement.classList.add('chat-message');
var avatarElement = document.createElement('i');
var avatarText = document.createTextNode(message.sender[0]);
avatarElement.appendChild(avatarText);
avatarElement.style['background-color'] = getAvatarColor(message.sender);
messageElement.appendChild(avatarElement);
var usernameElement = document.createElement('span');
var usernameText = document.createTextNode(message.sender);
usernameElement.appendChild(usernameText);
messageElement.appendChild(usernameElement);
}
var textElement = document.createElement('p');
var messageText = document.createTextNode(message.content);
textElement.appendChild(messageText);
messageElement.appendChild(textElement);
messageArea.appendChild(messageElement);
messageArea.scrollTop = messageArea.scrollHeight;
}
function getAvatarColor(messageSender) {
var hash = 0;
for (var i = 0; i < messageSender.length; i++) {
hash = 31 * hash + messageSender.charCodeAt(i);
}
var index = Math.abs(hash % colors.length);
return colors[index];
}
usernameForm.addEventListener('submit', connect, true)
messageForm.addEventListener('submit', sendMessage, true)
I want to realize that multiple chat rooms exist at the same time, So i do not use the #Sendto
I define a input room_num to indicate the number of the chat room
In js file, use stompClient.send("/app/chat.sendMessage/"+room_num, {}, JSON.stringify(chatMessage)); to send message.
In Controller, use simpMessagingTemplate.convertAndSend("/topic/public/"+room, chatMessage); receive message from different chatroom
But this way of stitching url will produce a lot hard code
Is there any good alternative?
When the user leaves the chat room, handleWebSocketDisconnectListener use messagingTemplate.convertAndSend("/topic/public", chatMessage); to notify users in a same chatroom, someone leave this room,
but the url should be /topic/public/room_num.
However, this is a DisconnectListener, It will listen for all closed connections,
I want to realize that when the user leaves a chat room, only notify the users in the same chat room that the user has left
Related
In the server end, I use Spring-websocket, Handshake as follow:
public class WebsocketEndPoint extends TextWebSocketHandler {
#Override
protected void handleTextMessage(WebSocketSession session,
TextMessage message) throws Exception {
System.out.println("start to translate data!");
super.handleTextMessage(session, message);
for (int i = 0; i <= 1000; i++) {
session.sendMessage(new TextMessage("push message " + i));
Thread.sleep(2000);
}
session.sendMessage(message);
}
#Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
System.out.println("Connection Established!");
}
#Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
System.out.println("Connection Closed!");
}
}
springmvc.xml as follow:
<mvc:default-servlet-handler/>
<bean id="websocket" class="com.yyp.mvc.controller.WebsocketEndPoint" />
<websocket:handlers>
<websocket:mapping path="/websocket" handler="websocket" />
<websocket:handshake-interceptors>
<bean class="com.yyp.mvc.controller.HandshakeInterceptor" />
</websocket:handshake-interceptors>
</websocket:handlers>
In the client, my html as follow:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
</head>
<body>
<button onclick="closeSocket()">stop socket</button>
<script>
var wsServer = 'ws://localhost:8080/websocket';
var websocket = new WebSocket(wsServer);
websocket.onopen = function (evt) { onOpen(evt) };
websocket.onclose = function (evt) { onClose(evt) };
websocket.onmessage = function (evt) { onMessage(evt) };
websocket.onerror = function (evt) { onError(evt) };
function onOpen(evt) {
console.log("Connected to WebSocket server.");
websocket.send('hello');
}
function onClose(evt) {
console.log("Disconnected");
}
function onMessage(evt) {
console.log('Retrieved data from server: ' + evt.data);
}
function onError(evt) {
console.log('Error occured: ' + evt.data);
}
function closeSocket (argument) {
console.log("close socket");
websocket.close();
websocket=null;
}
</script>
</body>
</html>
Finally, I could get log like this in the server:
before shakeHands
after shakeHands
Connection Established!
start to translate data!
Why couldn't get the "Connection Closed!" log after I click the "stop socket" button or how can I process the connection close information form the client?
I have a primafaces ring.I want to take images in xhtml codes.I have a papers library include images(paper1.jpg,paper2.jpg,..paper12.jpg). I don't want to use ringview.java.I added graphicimage tags between p:ring tags.But I don't display images.How can I do?
<h:form id="form">
<p:ring id="custom" value="#{ringView.papers}" var="paper">
<p:graphicImage id="img" name="papers/#{paper}" style="width:100%; height:100%"/>
</p:ring>
</h:form>
RingView.java
#ManagedBean
public class RingView implements Serializable {
private List<String> papers;
private String selectedPaper;
#PostConstruct
public void init() {
papers = new ArrayList<String>();
for (int i = 1; i <= 12; i++) {
papers.add("paper" + i + ".jpg");
}
}
public List<String> getPapers() {
return papers;
}
public void setPapers(List<String> papers) {
this.papers = papers;
}
public String getSelectedPaper() {
return selectedPaper;
}
public void setSelectedCar(String selectedPaper) {
this.selectedPaper = selectedPaper;
}
}
I tried something like:
<h:form id="form">
<p:ring id="custom">
<p:graphicImage id="img1" name="papers/paper1.jpg" style="width:100%; height:100%"/>
<p:graphicImage id="img2" name="papers/paper2.jpg" style="width:100%; height:100%"/>
<p:graphicImage id="img3" name="papers/paper3.jpg" style="width:100%; height:100%"/>
</p:ring>
In my current spring-boot application, I have this configuration for the thymeleaf:
#Configuration
public class Thymeleaf {
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
final Set<IDialect> dialects = new HashSet<IDialect>();
dialects.add( new SpringSecurityDialect() );
dialects.add( new FormDialect() );
dialects.add( new FieldDialect() );
engine.setDialects( dialects );
return engine;
}
}
and I add this two dialects:
org.store.custom.thymeleaf.dialect.FormDialect.java
public class FormDialect extends AbstractDialect {
public FormDialect() {
super();
}
//
// All of this dialect's attributes and/or tags
// will start with 'form:'
//
public String getPrefix() {
return "form";
}
//
// The processors.
//
#Override
public Set<IProcessor> getProcessors() {
final Set<IProcessor> processor = new HashSet<IProcessor>();
processor.add(new Form());
return processor;
}
}
org.store.custom.thymeleaf.dialect.FieldDialect.java
public class FieldDialect extends AbstractDialect {
public FieldDialect() {
super();
}
//
// All of this dialect's attributes and/or tags
// will start with 'field:'
//
public String getPrefix() {
return "field";
}
//
// The processors.
//
#Override
public Set<IProcessor> getProcessors() {
final Set<IProcessor> processor = new HashSet<IProcessor>();
processor.add(new Checkbox());
processor.add(new DataList());
processor.add(new Input());
processor.add(new Property());
processor.add(new Radio());
processor.add(new Select());
processor.add(new Textarea());
return processor;
}
}
but when I use a tag with those dialects on my html pages, this tags aren't evaluated to the normal tag. The page have a code like this:
<!DOCTYPE html>
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"
xmlns:form=""
xmlns:field="">
<head>
<title>Cadastro</title>
</head>
<body>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Cadastrar <span th:text="${command.getClass().getSimpleName()}"/></h3>
</div>
<div class="panel-body">
<form:form>
<field-box th:each="item : ${command.getClass().getDeclaredFields()}">
<div th:each="item2 : ${item.getDeclaredAnnotations()}">
<div th:switch="${item2.annotationType().getSimpleName()}">
<div th:case="'Checkbox'"><field:checkbox/></div>
<div th:case="'DataList'"><field:datalist/></div>
<div th:case="'Input'"><field:input/></div>
<div th:case="'Radiobutton'"><field:radio/></div>
<div th:case="'Select'"><field:select/></div>
<div th:case="'Textarea'"><field:textarea/></div>
</div>
</div>
</field-box>
</form:form>
</div>
</div>
<script src="js/form_submit.js"></script>
<script src="js/form_valida.js"></script>
</body>
</html>
and the code for the processor is like that:
public class Form extends AbstractProcessor {
#Override
public ProcessorResult doProcess(Arguments arguments,ProcessorMatchingContext context,Node node) {
Element form = new Element("form");
form.setProcessable(true);
form.setAttribute("role", "form");
form.setAttribute("class", "form");
form.setAttribute("action", "");
form.setAttribute("method", "post");
node.getParent().insertBefore(node, form);
return ProcessorResult.OK;
}
#Override
public int getPrecedence() {
return 0;
}
#Override
public IProcessorMatcher<? extends Node> getMatcher() {
return new ElementNameProcessorMatcher("form");
}
}
In this moment, my main idea about the reason this don't work it's because I don't use the correct value for the namepace. I try this values:
* http://form and http://field
* http://org for both
* http://org.store.custom.thymeleaf for both
* http://www.thymeleaf.org/org for both
Anyone can see what I am missing here?
This problem was solved when I change the Thymeleaf class to this:
#Configuration
public class Thymeleaf {
#Autowired
private FormDialect formDialect;
#Autowired
private FieldDialect fieldDialect;
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
final Set<IDialect> dialects = new HashSet<IDialect>();
dialects.add( new SpringSecurityDialect() );
dialects.add( formDialect );
dialects.add( fieldDialect );
engine.setDialects( dialects );
return engine;
}
#Bean
public FormDialect formDialect() {
return new FormDialect();
}
#Bean
public FieldDialect fieldDialect() {
return new FieldDialect();
}
}
I spent 10 years writing ASP/Ajax and MUST switch to JAVA.
I am starting very simple script so as to understand the concepts clearly before diving into more complex stuff. I am using Tomcat 7, Servlet 3
The problem is. The servlet is called successfully but the form data is not transferred
This is the edited servlet (Main.java)
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class Main
*/
#WebServlet("/Main")
public class Main extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* #see HttpServlet#HttpServlet()
*/
public Main() {
super();
// TODO Auto-generated constructor stub
}
/**
* #see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
//int ids;
//String ids;
//response.setContentType("text/html;charset=UTF-8");
//PrintWriter out = response.getWriter();
String val = request.getParameter("id");
String name = request.getParameter("name");
//response.getWriter().write(val);
//response.getWriter().write("::");
//response.getWriter().write(name);
response.setContentType("text/plain");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(val +" "+ name);
//System.out.print(val);
//if(val != null){
// //ids = Integer.parseInt(val);
// //out.print(ids);
// out.print(val);
//}
}
}
This is the edited JSP (Main.jsp):
<!DOCTYPE html>
<html lang="en">
<head>
<title>SO question 4112686</title>
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script>
$.ajaxSetup({ cache: false });
$(document).ready(function() {
$('#submit').click(function(event) {
var form = (event.target.form),
url = "/Main"
+ "?lName=" + escape(form.elements.lName.text)
+ "&fName=" + escape(form.elements.fName.text);
$.get(url, function(getData) {
$('#somediv').text(getData);
});
});
});
</script>
</head>
<body>
<form id="Main" action="Main" method="post">
<h4>AJAX Demo using Jquery in JSP and Servlet</h4>
Enter your Name:
<br/><input type="text" id="fName" name="fName"/>
<br/><input type="text" id="lName" name="lName"/>
<br/><input type="submit" id="submit" value="Ajax Submit"/>
<br/>
</form>
<div id="somediv">....</div>
</body>
</html>
Edit: If the form data is not transferred, that's because you are not sending it to the server. A GET request means you need lName=abc&fName=def appended to the URL:
<script>
$.ajaxSetup({ cache: false });
$(document).ready(function() {
$('#submit').click(function(event) {
var form = (event.target.form),
url = "/SomeServlet"
+ "?lName=" + escape(form.elements.lName.text)
+ "&fName=" + escape(form.elements.fName.text);
$.get(url, function(getData) {
$('#somediv').text(getData);
});
});
});
</script>
Core Ajax for State and country combo box to be used in JSP.
ajaxTest.jsp
<%#page contentType="text/html" pageEncoding="UTF-8" import="com.test.AjaxClass.*"%>
AJAX Page
var XmlHttp=false;
function CreateXmlHttp()
{
try
{
XmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); //for IE6
}
catch(e)
{
try
{
XmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
catch(oc)
{
XmlHttp = new XMLHttpRequest();//for browser mozila, opera, firefox.
}
}
}
function cState(){
var countid = document.getElementById('country').value;
CreateXmlHttp();
XmlHttp.onreadystatechange=HandleResponse;
XmlHttp.open("GET", "any.jsp?r=Math.random()&countid="+countid, true);
XmlHttp.send(null);
}
function HandleResponse(){
var stateobj = document.getElementById("state");
stateobj.options.length = 0;
if(XmlHttp.readyState==4 || XmlHttp.readyState=="complete"){
var XmlRoot = XmlHttp.responseXML.documentElement;
var xRows = XmlRoot.getElementsByTagName("check");
for(var i=0; i<xRows.length; i++){
var stateid = xRows[i].childNodes[0].firstChild.nodeValue;
var statename = xRows[i].childNodes[1].firstChild.nodeValue;
stateobj.options[i] = new Option(statename,stateid);
}
}
}
</script>
</head>
<body>
<select onchange="cState();" name="country" id="country">
<option value="0">Select Country</option>
<%
for (CountryClass cc : ajax.getCoutryList()) {
%>
<option value="<%=cc.getCountryid()%>"><%=cc.getCountryName()%></option>
<% }
%>
</select>
<select name="state" id="state">
</select>
</body>
any.jsp
<?xml version="1.0"?>
<%#page contentType="text/xml" pageEncoding="UTF-8" import="com.test.AjaxClass.*"%>
<%
int countid = Integer.parseInt(request.getParameter("countid"));
//System.out.println("tt :: " + countid);
java.util.List statelist = call.changeState(countid);
//System.out.println("length ::" + statelist.size());
for (StateClass sc : statelist) {
%>
<%=sc.getStateid()%>
<%=sc.getStateName()%>
<%
}
%>
AjaxClass.java
package com.test;
import java.util.ArrayList;
import java.util.List;
public class AjaxClass {
private List<CountryClass> coutryList = new ArrayList<CountryClass>();
public List<CountryClass> getCoutryList() {
coutryList.add(new CountryClass(1, "India"));
coutryList.add(new CountryClass(2, "Pakistan"));
coutryList.add(new CountryClass(3, "Bangladesh"));
coutryList.add(new CountryClass(4, "U.A.E."));
return coutryList;
}
public void setCoutryList(List<CountryClass> coutryList) {
this.coutryList = coutryList;
}
public class CountryClass {
public Integer countryid;
public String countryName;
public String getCountryName() {
return countryName;
}
public void setCountryName(String countryName) {
this.countryName = countryName;
}
public Integer getCountryid() {
return countryid;
}
public void setCountryid(Integer countryid) {
this.countryid = countryid;
}
public CountryClass(Integer countryid, String countryName) {
this.countryid = countryid;
this.countryName = countryName;
}
}
private List<StateClass> stateList = new ArrayList<StateClass>();
public List<StateClass> getStateList() {
stateList.add(new StateClass(1, 1, "Gujarat"));
stateList.add(new StateClass(2, 1, "Maharashtra"));
stateList.add(new StateClass(3, 2, "Karachi"));
stateList.add(new StateClass(4, 2, "Lahore"));
stateList.add(new StateClass(5, 3, "Dhaka"));
stateList.add(new StateClass(6, 3, "Chittagong"));
stateList.add(new StateClass(7, 4, "Dubai"));
stateList.add(new StateClass(8, 4, "Behrin"));
stateList.add(new StateClass(9, 4, "Sarjah"));
return stateList;
}
public void setStateList(List<StateClass> stateList) {
this.stateList = stateList;
}
public class StateClass {
Integer stateid;
Integer countryref;
String stateName;
public Integer getCountryref() {
return countryref;
}
public void setCountryref(Integer countryref) {
this.countryref = countryref;
}
public String getStateName() {
return stateName;
}
public void setStateName(String stateName) {
this.stateName = stateName;
}
public Integer getStateid() {
return stateid;
}
public void setStateid(Integer stateid) {
this.stateid = stateid;
}
public StateClass(Integer stateid, Integer countryref, String stateName) {
this.stateid = stateid;
this.countryref = countryref;
this.stateName = stateName;
}
}
public List<StateClass> changeState(Integer countryref) {
List<StateClass> newList = new ArrayList<AjaxClass.StateClass>();
for (StateClass stateClass : getStateList()) {
if (stateClass.countryref == countryref) {
newList.add(stateClass);
}
}
return newList;
}
}