/**
* OpenKM, Open Document Management System (http://www.openkm.com)
* Copyright (c) 2006-2011 Paco Avila & Josep Llort
*
* No bytes were intentionally harmed during the development of this application.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package com.openkm.servlet.frontend;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import com.openkm.frontend.client.service.OKMChatService;
import com.openkm.util.UUIDGenerator;
/**
* Servlet Class
*
* @web.servlet name="ChatServlet"
* display-name="Directory tree service"
* description="Directory tree service"
* @web.servlet-mapping url-pattern="/ChatServlet"
* @web.servlet-init-param name="A parameter"
* value="A value"
*/
public class ChatServlet extends OKMRemoteServiceServlet implements OKMChatService {
private static final long serialVersionUID = 3780857624687394918L;
private static final int DELAY = 100; // mseg
private static final int ACTION_LOGIN = 0;
private static final int ACTION_LOGOUT = 1;
private static final int ACTION_ADD_ROOM_TO_USER = 2;
private static final int ACTION_REMOVE_USER_ROOM = 3;
private static final int ACTION_ADD_PENDING_ROOM_TO_USER = 4;
private static final int ACTION_GET_PENDING_USER_ROOM = 5;
private static final int ACTION_GET_PENDING_USER_ROOM_MESSAGE = 6;
private static final int ACTION_ADD_USER_MESSAGE_TO_ROOM = 7;
private static final int ACTION_CREATE_MESSAGE_ROOM = 8;
private static final int ACTION_CREATE_MESSAGE_USER_ROOM = 9;
private static final int ACTION_REMOVE_USER_MESSAGE_ROOM = 10;
private static final int ACTION_DELETE_EMPTY_MESSAGE_ROOM = 11;
private static final int ACTION_GET_USERS_IN_MESSAGE_ROOM = 12;
private static List<String> usersLogged = new ArrayList<String>();
private static Map<String, List<String>> usersRooms= new HashMap<String, List<String>>(); // user is the key
private static Map<String, List<String>> pendingUsersRooms= new HashMap<String, List<String>>(); // user is the key
private static Map<String, HashMap<String,List<String>>> msgUsersRooms= new HashMap<String, HashMap<String,List<String>>>(); // room is the key
// user is the subkey, messages are copied to each user
@Override
public void init(final ServletConfig config) throws ServletException {
super.init(config);
}
@Override
public void login() {
usersLoggedAction(ACTION_LOGIN);
}
@Override
public void logout() {
usersLoggedAction(ACTION_LOGOUT);
}
@Override
public List<String> getLoggedUsers() {
return usersLogged;
}
@Override
public String createNewChatRoom(String user) {
updateSessionManager();
String room = UUIDGenerator.generate(""); // Used to unique identifying room
String actualUser = getThreadLocalRequest().getRemoteUser();
// Add users to rooms
usersRoomAction(room, user, ACTION_ADD_ROOM_TO_USER);
pendingRoomAction(room, user, ACTION_ADD_PENDING_ROOM_TO_USER);
usersRoomAction(room, actualUser, ACTION_ADD_ROOM_TO_USER);
messageUserRoomAction(room,"","",ACTION_CREATE_MESSAGE_ROOM);
messageUserRoomAction(room, user, "", ACTION_CREATE_MESSAGE_USER_ROOM);
messageUserRoomAction(room, actualUser, "", ACTION_CREATE_MESSAGE_USER_ROOM);
return room;
}
@Override
public List<String> getPendingMessage(String room) {
String user = getThreadLocalRequest().getRemoteUser();
List<String> pendingMessages = new ArrayList<String>();
int countCycle = 0;
updateSessionManager();
// 10 * Delay = 1000 = 1 second ( we want a 10 seconds waiting mantaining RPC comunication) that's 10*10=100 cycles
// With it mechanism
do {
pendingMessages = messageUserRoomAction(room, user, "", ACTION_GET_PENDING_USER_ROOM_MESSAGE);
countCycle++;
try {
Thread.sleep(DELAY);
} catch (InterruptedException e) {
e.printStackTrace();
}
} while (pendingMessages.isEmpty() && (countCycle<100) && usersLogged.contains(user));
return pendingMessages;
}
@Override
public List<String> getPendingChatRoomUser() {
String user = getThreadLocalRequest().getRemoteUser();
List<String> pendingRooms = new ArrayList<String>();
int countCycle = 0;
updateSessionManager();
// 10 * Delay = 1000 = 1 second ( we want a 10 seconds waiting mantaining RPC comunication) that's 10*10=100 cycles
// With it mechanism
do {
pendingRooms = pendingRoomAction("", user, ACTION_GET_PENDING_USER_ROOM);
countCycle++;
try {
Thread.sleep(DELAY);
} catch (InterruptedException e) {
e.printStackTrace();
}
} while (pendingRooms.isEmpty() && (countCycle<100) && usersLogged.contains(user));
return pendingRooms;
}
@Override
public void addMessageToRoom(String room, String msg) {
updateSessionManager();
String user = getThreadLocalRequest().getRemoteUser();
messageUserRoomAction(room, user, msg, ACTION_ADD_USER_MESSAGE_TO_ROOM);
}
@Override
public void closeRoom(String room) {
updateSessionManager();
String user = getThreadLocalRequest().getRemoteUser();
usersRoomAction(room, user, ACTION_REMOVE_USER_ROOM);
messageUserRoomAction(room, user, "", ACTION_REMOVE_USER_MESSAGE_ROOM);
messageUserRoomAction(room, "", "", ACTION_DELETE_EMPTY_MESSAGE_ROOM); // Evaluates if message room must be deleted
}
@Override
public void addUserToChatRoom(String room, String user) {
updateSessionManager();
usersRoomAction(room, user, ACTION_ADD_ROOM_TO_USER);
pendingRoomAction(room, user, ACTION_ADD_PENDING_ROOM_TO_USER);
messageUserRoomAction(room, user, "", ACTION_CREATE_MESSAGE_USER_ROOM);
}
@Override
public String usersInRoom(String room) {
updateSessionManager();
return String.valueOf(messageUserRoomAction(room, "", "", ACTION_GET_USERS_IN_MESSAGE_ROOM).size());
}
@Override
public List<String> getUsersInRoom(String room) {
updateSessionManager();
return messageUserRoomAction(room, "", "", ACTION_GET_USERS_IN_MESSAGE_ROOM);
}
/**
* Synchronized users logged actions
*/
private synchronized void usersLoggedAction(int action) {
String user = getThreadLocalRequest().getRemoteUser();
updateSessionManager();
switch (action) {
case ACTION_LOGIN:
if (!usersLogged.contains(user)) {
usersLogged.add(user);
}
Collections.sort(usersLogged); // Always we sort logged users
break;
case ACTION_LOGOUT:
if (usersLogged.contains(user)) {
usersLogged.remove(user);
}
if (pendingUsersRooms.containsKey(user)) {
pendingUsersRooms.remove(user);
}
if (usersRooms.containsKey(user)) {
List<String> rooms = usersRooms.get(user);
for (Iterator<String> it = rooms.iterator(); it.hasNext();) {
String room = it.next();
if (msgUsersRooms.containsKey(room)) {
Map<String, List<String>> roomMessages = msgUsersRooms.get(room);
if (roomMessages.containsKey(user)) {
roomMessages.remove(user);
}
}
}
}
break;
}
}
/**
* Synchronized users room actions
*/
private synchronized void usersRoomAction(String room, String user, int action) {
updateSessionManager();
switch (action) {
case ACTION_ADD_ROOM_TO_USER:
if (!usersRooms.keySet().contains(user)) {
List<String> userRoomList = new ArrayList<String>();
userRoomList.add(room);
usersRooms.put(user, userRoomList);
} else {
List<String> userRoomList = usersRooms.get(user);
if (!userRoomList.contains(room)) {
userRoomList.add(room);
}
}
break;
case ACTION_REMOVE_USER_ROOM:
if (usersRooms.keySet().contains(user)) {
List<String> userRoomList = usersRooms.get(user);
if (userRoomList.contains(room)) {
userRoomList.remove(room);
}
}
break;
}
}
/**
* Synchronized pending room actions
*/
private synchronized List<String> pendingRoomAction(String room, String user, int action) {
updateSessionManager();
switch(action) {
case ACTION_ADD_PENDING_ROOM_TO_USER:
if (!pendingUsersRooms.keySet().contains(user)) {
List<String> userPendingRoomList = new ArrayList<String>();
userPendingRoomList.add(room);
pendingUsersRooms.put(user, userPendingRoomList);
} else {
List<String> userPendingRoomList = pendingUsersRooms.get(user);
if (!userPendingRoomList.contains(room)) {
userPendingRoomList.add(room);
}
}
return new ArrayList<String>();
case ACTION_GET_PENDING_USER_ROOM:
if (pendingUsersRooms.keySet().contains(user)) {
List<String> userRooms = pendingUsersRooms.get(user);
pendingUsersRooms.remove(user);
return userRooms;
} else {
return new ArrayList<String>();
}
default:
return new ArrayList<String>();
}
}
/**
* Synchronized message user room actions
*/
private synchronized List<String> messageUserRoomAction(String room, String user, String msg, int action) {
updateSessionManager();
switch(action) {
case ACTION_GET_PENDING_USER_ROOM_MESSAGE:
if (msgUsersRooms.containsKey(room) && msgUsersRooms.get(room).containsKey(user)) {
List<String> messages = msgUsersRooms.get(room).get(user);
msgUsersRooms.get(room).put(user, new ArrayList<String>()); // Empty messages
return messages;
} else {
return new ArrayList<String>();
}
case ACTION_ADD_USER_MESSAGE_TO_ROOM:
String message = user + ": " + msg;
if (msgUsersRooms.containsKey(room)) {
Map<String, List<String>> roomMap = msgUsersRooms.get(room);
for (Iterator<String> it = roomMap.keySet().iterator(); it.hasNext();) {
String roomUser = it.next();
// Pending message is not added to himself ( that's done by UI )
if (!roomUser.equals(user)) {
roomMap.get(roomUser).add(message); // Adding message for each user available
}
}
}
return new ArrayList<String>();
case ACTION_CREATE_MESSAGE_ROOM:
if (!msgUsersRooms.containsKey(room)) {
msgUsersRooms.put(room, new HashMap<String, List<String>>());
}
return new ArrayList<String>();
case ACTION_CREATE_MESSAGE_USER_ROOM:
if (msgUsersRooms.containsKey(room)) {
if (!msgUsersRooms.get(room).containsKey(user)) {
msgUsersRooms.get(room).put(user, new ArrayList<String>());
}
}
return new ArrayList<String>();
case ACTION_REMOVE_USER_MESSAGE_ROOM:
if (msgUsersRooms.containsKey(room)) {
if (msgUsersRooms.get(room).containsKey(user)) {
msgUsersRooms.get(room).remove(user);
}
}
return new ArrayList<String>();
case ACTION_DELETE_EMPTY_MESSAGE_ROOM:
// Room message without users must be deleted
if (msgUsersRooms.containsKey(room)) {
if (msgUsersRooms.get(room).keySet().size()==0) {
msgUsersRooms.remove(room);
}
}
return new ArrayList<String>();
case ACTION_GET_USERS_IN_MESSAGE_ROOM:
if (msgUsersRooms.containsKey(room)) {
Collection<String> userList = msgUsersRooms.get(room).keySet();
return new ArrayList<String>(userList);
} else {
return new ArrayList<String>();
}
default:
return new ArrayList<String>();
}
}
}