package org.eclipsecon.gwt.chattr.server;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import org.eclipsecon.gwt.chattr.client.ChatEvent;
import org.eclipsecon.gwt.chattr.client.ChatEventVisitor;
import org.eclipsecon.gwt.chattr.client.ChatService;
import org.eclipsecon.gwt.chattr.client.ChatServiceException;
import org.eclipsecon.gwt.chattr.client.NewMessageEvent;
import org.eclipsecon.gwt.chattr.client.User;
import org.eclipsecon.gwt.chattr.client.UserAddEvent;
import org.eclipsecon.gwt.chattr.client.UserRemoveEvent;
import org.eclipsecon.gwt.chattr.client.UserUpdateEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
/**
* Servlet which implements the {@link ChatService} service.
*/
public class ChatServiceImpl extends RemoteServiceServlet implements
ChatService {
/**
*
*/
private final class ChatEventHandler extends ChatEventVisitor {
public void visit(NewMessageEvent event) {
List recipients = event.getRecipients();
if (recipients != null) {
propagateEvent(recipients.iterator(), event);
}
}
public void visit(UserAddEvent event) {
// The server sends this message but it should never receive it
}
public void visit(UserRemoveEvent event) {
// The server should send this message but it should never receive it
}
public void visit(UserUpdateEvent event) {
Set keys = userToUserInfoMap.keySet();
keys.remove(event.getUser());
UserInfo userInfo = findUserInfoForUser(event.getUser());
assert (userInfo != null);
userInfo.user.setStatusMessage(event.getUser().getStatusMessage());
propagateEvent(keys.iterator(), event);
}
}
private final class SuperUserInfo extends UserInfo {
public SuperUserInfo(User user) {
super(user);
}
public void addEvent(ChatEvent event) {
if (event instanceof NewMessageEvent) {
NewMessageEvent newMsgEvt = (NewMessageEvent) event;
UserInfo userInfo = findUserInfoForUser(newMsgEvt.getSender());
if (userInfo != null) {
userInfo.addEvent(new NewMessageEvent(getUser(), new ArrayList(
Collections.singletonList(newMsgEvt.getSender())),
getUser().getStatusMessage()));
}
}
}
}
/**
*
*/
private class UserInfo {
private static final long DEFAULT_INACTIVITY_TIMEOUT_MILLIS = 10000;
private List eventQueue = new ArrayList();
private TimerTask inactivityTimerTask;
private final User user;
public UserInfo(User user) {
this.user = user;
}
public void addEvent(ChatEvent event) {
eventQueue.add(event);
}
public void clearEventQueue() {
eventQueue = new ArrayList();
}
public List getEventQueue() {
return eventQueue;
}
public User getUser() {
return user;
}
public void resetInactivityTimer() {
if (inactivityTimerTask != null) {
inactivityTimerTask.cancel();
}
inactivityTimerTask = new TimerTask() {
public void run() {
userToUserInfoMap.remove(user);
fireUserRemovedEvent(user);
}
};
try {
inactivityTimer.schedule(inactivityTimerTask,
DEFAULT_INACTIVITY_TIMEOUT_MILLIS);
} catch (IllegalStateException ex) {
ex.printStackTrace();
}
}
}
private ChatEventHandler eventHandler = new ChatEventHandler();
/**
*
*/
private Timer inactivityTimer = new Timer();
/**
* Map of user names to event queues
*/
private final Map userToUserInfoMap = new HashMap();
public ChatServiceImpl() {
// Default to George P. Burdell
User user = new User("George P. Burdell", "Go Jackets!");
UserInfo userInfo = new SuperUserInfo(user);
userToUserInfoMap.put(user, userInfo);
}
/**
*
* <ul>
* <li>Get the user - if non-existant create the user and add an event to all
* of the other user's event queues</li>
* <li>Kick the inactivity timer</li>
* <li>Process all of the events sent by this user user</li>
* <li>Get all of the events queued for this user</li>
* <li>clear the user's event queue</li>
* </ul>
*
* @throws ChatServiceException
*/
public List exchangeEvents(User user, List clientEvents)
throws ChatServiceException {
if (user == null) {
// RuntimeExceptions are not serialized by default
throw new ChatServiceException("User cannot be null");
}
UserInfo userInfo = findUserInfoForUser(user);
if (userInfo == null) {
userInfo = createUser(user);
}
userInfo.resetInactivityTimer();
if (clientEvents != null) {
processClientEvents(clientEvents);
}
List events = userInfo.getEventQueue();
userInfo.clearEventQueue();
return events;
}
public List getCurrentUsers(User user) {
UserInfo userInfo = findUserInfoForUser(user);
if (userInfo == null) {
userInfo = createUser(user);
}
List currentUsers = new ArrayList(userToUserInfoMap.keySet());
currentUsers.remove(user);
return currentUsers;
}
private UserInfo createUser(User user) {
UserInfo userInfo = new UserInfo(user);
fireNewUserEvent(user);
notifyNewUserOfExistingUsers(userInfo);
userToUserInfoMap.put(user, userInfo);
return userInfo;
}
/**
* Returns the {@link UserInfo} for a given {@link User}.
*
* @param user the user whose UserInfo we want to lookup
* @return UserInfo for the given user or null, if no such user exists
*/
private UserInfo findUserInfoForUser(User user) {
return (UserInfo) userToUserInfoMap.get(user);
}
/**
*
* @param user
*/
private void fireNewUserEvent(User user) {
UserAddEvent userAddEvent = new UserAddEvent(user);
propagateEvent(userToUserInfoMap.keySet().iterator(), userAddEvent);
}
/**
*
* @param user
*/
private void fireUserRemovedEvent(User user) {
UserRemoveEvent userRemovedEvent = new UserRemoveEvent(user);
propagateEvent(userToUserInfoMap.keySet().iterator(), userRemovedEvent);
}
private void notifyNewUserOfExistingUsers(UserInfo newUserInfo) {
Iterator iter = userToUserInfoMap.keySet().iterator();
while (iter.hasNext()) {
User user = (User) iter.next();
newUserInfo.addEvent(new UserAddEvent(user));
}
}
/**
* Processes the list of events that a client sent.
*
* @param clientEvents list of events that a client sent
*/
private void processClientEvents(List clientEvents) {
assert (clientEvents != null);
Iterator iter = clientEvents.iterator();
while (iter.hasNext()) {
ChatEvent chatEvent = (ChatEvent) iter.next();
chatEvent.accept(eventHandler);
}
}
/**
* Propagates an event to a set of users.
*
* @param iterRecipients iterator over set of recipients
* @param event the event that we want to propagate
*/
private void propagateEvent(Iterator iterRecipients, ChatEvent event) {
while (iterRecipients.hasNext()) {
User recipient = (User) iterRecipients.next();
UserInfo userInfo = findUserInfoForUser(recipient);
if (userInfo != null) {
userInfo.addEvent(event);
}
}
}
}