remove dead clients

refactor the injector out to the servlet context
This commit is contained in:
Andy Janata 2012-01-06 15:53:55 -08:00
parent 9508575949
commit 8a45078d77
7 changed files with 153 additions and 31 deletions

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<display-name>cah</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>TestServlet</servlet-name>
<servlet-class>net.socialgamer.cah.TestServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>LongPollServlet</servlet-name>
<servlet-class>net.socialgamer.cah.LongPollServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>AjaxServlet</servlet-name>
<servlet-class>net.socialgamer.cah.AjaxServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>Schema</servlet-name>
<servlet-class>net.socialgamer.cah.Schema</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TestServlet</servlet-name>
<url-pattern>/TestServlet</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>LongPollServlet</servlet-name>
<url-pattern>/LongPollServlet</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>AjaxServlet</servlet-name>
<url-pattern>/AjaxServlet</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Schema</servlet-name>
<url-pattern>/Schema</url-pattern>
</servlet-mapping>
<listener>
<listener-class>net.socialgamer.cah.StartupUtils</listener-class>
</listener>
</web-app>

View File

@ -4,11 +4,8 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
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 javax.servlet.http.HttpSession;
@ -16,9 +13,6 @@ import javax.servlet.http.HttpSession;
import net.socialgamer.cah.handlers.Handler;
import net.socialgamer.cah.handlers.Handlers;
import com.google.inject.Guice;
import com.google.inject.Injector;
/**
* Servlet implementation class AjaxServlet
@ -29,25 +23,6 @@ import com.google.inject.Injector;
public class AjaxServlet extends CahServlet {
private static final long serialVersionUID = 1L;
private final Injector injector;
/**
* @see HttpServlet#HttpServlet()
*/
public AjaxServlet() {
super();
injector = Guice.createInjector();
}
/**
* @see Servlet#init(ServletConfig)
*/
@Override
public void init(final ServletConfig config) throws ServletException {
// TODO Auto-generated method stub
}
/**
* @see CahServlet#doPost(HttpServletRequest request, HttpServletResponse response, HttpSession
* hSession)
@ -76,7 +51,7 @@ public class AjaxServlet extends CahServlet {
final Handler handler;
try {
handler = injector.getInstance(Handlers.LIST.get(op));
handler = getInjector().getInstance(Handlers.LIST.get(op));
} catch (final Exception e) {
returnError(out, "bad_op", "Invalid operation.", serial);
return;

View File

@ -8,7 +8,6 @@ public class CahModule extends AbstractModule {
@Override
protected void configure() {
// TODO Auto-generated method stub
bind(Server.class).in(Singleton.class);
}
}

View File

@ -13,6 +13,8 @@ import javax.servlet.http.HttpSession;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import com.google.inject.Injector;
/**
* Servlet implementation class CahServlet
@ -109,4 +111,7 @@ public abstract class CahServlet extends HttpServlet {
writer.println(JSONValue.toJSONString(data));
}
protected Injector getInjector() {
return (Injector) getServletContext().getAttribute(StartupUtils.INJECTOR);
}
}

View File

@ -0,0 +1,48 @@
package net.socialgamer.cah;
import java.util.Timer;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.servlet.GuiceServletContextListener;
public class StartupUtils extends GuiceServletContextListener {
public static final String INJECTOR = "injector";
private static final long PING_START_DELAY = 60 * 1000;
private static final long PING_CHECK_DELAY = 5 * 1000;
private static final String PING_TIMER_NAME = "ping_timer";
@Override
public void contextDestroyed(final ServletContextEvent contextEvent) {
final ServletContext context = contextEvent.getServletContext();
final Timer timer = (Timer) context.getAttribute(PING_TIMER_NAME);
assert (timer != null);
timer.cancel();
context.removeAttribute(PING_TIMER_NAME);
context.removeAttribute(INJECTOR);
}
@Override
public void contextInitialized(final ServletContextEvent contextEvent) {
final ServletContext context = contextEvent.getServletContext();
final Injector injector = getInjector();
final UserPing ping = injector.getInstance(UserPing.class);
final Timer timer = new Timer();
timer.schedule(ping, PING_START_DELAY, PING_CHECK_DELAY);
context.setAttribute(PING_TIMER_NAME, timer);
context.setAttribute(INJECTOR, injector);
}
@Override
protected Injector getInjector() {
return Guice.createInjector();
}
}

View File

@ -0,0 +1,23 @@
package net.socialgamer.cah;
import java.util.TimerTask;
import net.socialgamer.cah.data.ConnectedUsers;
import com.google.inject.Inject;
public class UserPing extends TimerTask {
private final ConnectedUsers users;
@Inject
public UserPing(final Server server) {
users = server.getConnectedUsers();
}
@Override
public void run() {
users.checkForPingTimeouts();
}
}

View File

@ -2,9 +2,11 @@ package net.socialgamer.cah.data;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import net.socialgamer.cah.data.QueuedMessage.Type;
import net.socialgamer.cah.data.User.DisconnectReason;
/**
@ -16,6 +18,11 @@ import net.socialgamer.cah.data.QueuedMessage.Type;
*/
public class ConnectedUsers {
/**
* Duration of a ping timeout, in nanoseconds.
*/
public static final long PING_TIMEOUT = 3L * 60L * 1000L * 1000000L;
private final Map<String, User> users = new HashMap<String, User>();
public boolean hasUser(final String userName) {
@ -33,10 +40,31 @@ public class ConnectedUsers {
}
public void removeUser(final User user, final User.DisconnectReason reason) {
// TODO fire an event for a disconnected user to interested parties
// synchronized (users) {
//
// }
synchronized (users) {
users.remove(user.getNickname());
notifyRemoveUser(user, reason);
}
}
private void notifyRemoveUser(final User user, final User.DisconnectReason reason) {
final HashMap<String, Object> data = new HashMap<String, Object>();
data.put("event", "player_leave");
data.put("nickname", user.getNickname());
data.put("reason", reason.toString());
broadcastToAll(Type.PLAYER_DISCONNECT, data);
}
public void checkForPingTimeouts() {
synchronized (users) {
final Iterator<User> iterator = users.values().iterator();
while (iterator.hasNext()) {
final User u = iterator.next();
if (System.nanoTime() - u.getLastHeardFrom() > PING_TIMEOUT) {
notifyRemoveUser(u, DisconnectReason.PING_TIMEOUT);
iterator.remove();
}
}
}
}
/**