remove dead clients
refactor the injector out to the servlet context
This commit is contained in:
parent
9508575949
commit
8a45078d77
|
@ -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>
|
|
@ -4,11 +4,8 @@ import java.io.IOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.servlet.Servlet;
|
|
||||||
import javax.servlet.ServletConfig;
|
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.annotation.WebServlet;
|
import javax.servlet.annotation.WebServlet;
|
||||||
import javax.servlet.http.HttpServlet;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import javax.servlet.http.HttpSession;
|
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.Handler;
|
||||||
import net.socialgamer.cah.handlers.Handlers;
|
import net.socialgamer.cah.handlers.Handlers;
|
||||||
|
|
||||||
import com.google.inject.Guice;
|
|
||||||
import com.google.inject.Injector;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Servlet implementation class AjaxServlet
|
* Servlet implementation class AjaxServlet
|
||||||
|
@ -29,25 +23,6 @@ import com.google.inject.Injector;
|
||||||
public class AjaxServlet extends CahServlet {
|
public class AjaxServlet extends CahServlet {
|
||||||
private static final long serialVersionUID = 1L;
|
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
|
* @see CahServlet#doPost(HttpServletRequest request, HttpServletResponse response, HttpSession
|
||||||
* hSession)
|
* hSession)
|
||||||
|
@ -76,7 +51,7 @@ public class AjaxServlet extends CahServlet {
|
||||||
|
|
||||||
final Handler handler;
|
final Handler handler;
|
||||||
try {
|
try {
|
||||||
handler = injector.getInstance(Handlers.LIST.get(op));
|
handler = getInjector().getInstance(Handlers.LIST.get(op));
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
returnError(out, "bad_op", "Invalid operation.", serial);
|
returnError(out, "bad_op", "Invalid operation.", serial);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -8,7 +8,6 @@ public class CahModule extends AbstractModule {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
// TODO Auto-generated method stub
|
|
||||||
bind(Server.class).in(Singleton.class);
|
bind(Server.class).in(Singleton.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,8 @@ import javax.servlet.http.HttpSession;
|
||||||
import org.json.simple.JSONObject;
|
import org.json.simple.JSONObject;
|
||||||
import org.json.simple.JSONValue;
|
import org.json.simple.JSONValue;
|
||||||
|
|
||||||
|
import com.google.inject.Injector;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Servlet implementation class CahServlet
|
* Servlet implementation class CahServlet
|
||||||
|
@ -109,4 +111,7 @@ public abstract class CahServlet extends HttpServlet {
|
||||||
writer.println(JSONValue.toJSONString(data));
|
writer.println(JSONValue.toJSONString(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Injector getInjector() {
|
||||||
|
return (Injector) getServletContext().getAttribute(StartupUtils.INJECTOR);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,9 +2,11 @@ package net.socialgamer.cah.data;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import net.socialgamer.cah.data.QueuedMessage.Type;
|
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 {
|
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>();
|
private final Map<String, User> users = new HashMap<String, User>();
|
||||||
|
|
||||||
public boolean hasUser(final String userName) {
|
public boolean hasUser(final String userName) {
|
||||||
|
@ -33,10 +40,31 @@ public class ConnectedUsers {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeUser(final User user, final User.DisconnectReason reason) {
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue