package edu.harvard.econcs.turkserver.server;
import java.io.IOException;
import java.util.Map;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import org.cometd.annotation.*;
import org.cometd.bayeux.Message;
import org.cometd.bayeux.server.*;
import org.cometd.server.BayeuxServerImpl;
import org.cometd.server.authorizer.GrantAuthorizer;
import org.cometd.server.ext.AcknowledgedMessagesExtension;
import org.cometd.server.ext.TimesyncExtension;
import edu.harvard.econcs.turkserver.Codec;
import edu.harvard.econcs.turkserver.schema.Quiz;
public class SessionServlet extends GenericServlet {
private static final long serialVersionUID = -3882966106597782108L;
protected JettyCometD jettyServer;
protected SessionServer sessions;
protected BayeuxServerImpl bayeux;
protected ServerAnnotationProcessor processor;
@Override
public void init() throws ServletException {
super.init();
sessions = (SessionServer) getServletContext().getAttribute(SessionServer.ATTRIBUTE);
jettyServer = (JettyCometD) getServletContext().getAttribute(JettyCometD.ATTRIBUTE);
bayeux = (BayeuxServerImpl) getServletContext().getAttribute(BayeuxServer.ATTRIBUTE);
// Create extensions
bayeux.addExtension(new TimesyncExtension());
bayeux.addExtension(new AcknowledgedMessagesExtension());
// Wildcard authorizer - Deny unless granted
bayeux.createIfAbsent("/**",new ServerChannel.Initializer()
{
public void configureChannel(ConfigurableServerChannel channel)
{
channel.addAuthorizer(GrantAuthorizer.GRANT_NONE);
}
});
// Allow anybody to handshake
bayeux.getChannel(ServerChannel.META_HANDSHAKE).addAuthorizer(GrantAuthorizer.GRANT_PUBLISH);
processor = new ServerAnnotationProcessor(bayeux);
// Debugging service
processor.process(new Monitor());
processor.process(new UserData());
processor.process(new ExperimentData());
// Watch for connect/disconnects
bayeux.addListener(sessions.new UserSessionListener());
}
public ServerAnnotationProcessor getProcessor() {
return processor;
}
@Service("userdata")
public class UserData {
@Configure ("/service/user")
protected void configureUser(ConfigurableServerChannel channel)
{
channel.addAuthorizer(GrantAuthorizer.GRANT_PUBLISH);
channel.setPersistent(true);
}
@Listener("/service/user")
public void userStatus(ServerSession session, ServerMessage message) {
Map<String, Object> data = message.getDataAsMap();
String status = data.get("status").toString();
String clientId = session.getId();
/*
* TODO compare this hitId with the session metadata
* Remove requirements for client to send hitId every time
*
* TODO make this logger class the same as the other ones
*/
String hitId = null;
try { hitId = data.get("hitId").toString(); } catch (NullPointerException e) {}
if( Codec.hitView.equals(status) ) {
sessions.logger.info("HIT " + hitId + " is being viewed by " + clientId);
sessions.sessionView(session, hitId);
}
else if( Codec.hitAccept.equals(status)) {
String assignmentId = null, workerId = null;
try { assignmentId = data.get("assignmentId").toString(); }
catch (NullPointerException e) { sessions.logger.warn("Null assignmentId for " + clientId); }
try { workerId = data.get("workerId").toString(); }
catch (NullPointerException e) { sessions.logger.warn("Null workerId for " + clientId);}
sessions.logger.info("HIT " + hitId + " assignment " + assignmentId + " accepted by " + workerId);
sessions.sessionAccept(session, hitId, assignmentId, workerId);
}
else if( Codec.quizResults.equals(status) ) {
int correct = Integer.parseInt(data.get("correct").toString());
int total = Integer.parseInt(data.get("total").toString());
Quiz qr = new Quiz();
qr.setNumCorrect(correct);
qr.setNumTotal(total);
qr.setScore(1.0 * correct / total);
qr.setAnswers(data.get("answers").toString());
sessions.rcvQuizResults(session, qr);
}
else if( "inactive".equals(status) ) {
long inactiveStart = ((Number) data.get("start")).longValue();
long inactiveTime = ((Number) data.get("time")).longValue();
sessions.rcvInactiveTime(session, inactiveStart, inactiveTime);
}
else if( Codec.hitSubmit.equals(status) ) {
String survey = (String) data.get("comments");
sessions.sessionSubmit(session, survey);
}
else {
sessions.logger.warn("Unrecognized message " + data);
}
}
}
@Service("experiment")
public class ExperimentData {
@Configure("/service/experiment/*")
public void configureServiceExperiment(ConfigurableServerChannel channel) {
// TODO only allow pub/sub for clients that are part of this experiment
channel.addAuthorizer(GrantAuthorizer.GRANT_SUBSCRIBE_PUBLISH);
}
@Listener("/service/experiment/*")
public void listenServiceExperiment(ServerSession session, ServerMessage message) {
// Deliver this to the appropriate experiment server
sessions.rcvExperimentServiceMsg(session, message.getDataAsMap());
}
@Configure("/experiment/*")
public void configureExperiment(ConfigurableServerChannel channel) {
// TODO only allow pub/sub for clients that are part of this experiment
// No need for persistent, leaves when last client is gone
channel.addAuthorizer(GrantAuthorizer.GRANT_SUBSCRIBE_PUBLISH);
}
@Listener("/experiment/*")
public boolean listenExperiment(ServerSession session, ServerMessage message) {
if( session.isLocalSession() ) {
// Always forward broadcast messages generated locally
return true;
}
else {
// Deliver this to the appropriate experiment server
return sessions.rcvExperimentBroadcastMsg(session, message.getDataAsMap());
}
}
}
@Service("monitor")
public class Monitor
{
@Listener("/meta/subscribe")
public void monitorSubscribe(ServerSession session, ServerMessage message)
{
sessions.logger.debug("Monitored Subscribe from "+session+" for "+message.get(Message.SUBSCRIPTION_FIELD));
}
@Listener("/meta/unsubscribe")
public void monitorUnsubscribe(ServerSession session, ServerMessage message)
{
sessions.logger.debug("Monitored Unsubscribe from "+session+" for "+message.get(Message.SUBSCRIPTION_FIELD));
}
@Listener("/meta/*")
public void monitorMeta(ServerSession session, ServerMessage message)
{
// if (sessions.logger.isDebugEnabled())
sessions.logger.debug(message.toString());
}
@Listener("/service/*")
public void monitorSvc(ServerSession session, ServerMessage message)
{
// if (sessions.logger.isDebugEnabled())
sessions.logger.debug(message.toString());
}
}
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
((HttpServletResponse) res).sendError(503);
}
}