<commit_before>package org.flowersinthesand.jquerysocket;

import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;

import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.WebSocketServlet;
import org.reflections.Reflections;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

@SuppressWarnings("serial")
public class DispatcherServlet extends WebSocketServlet {
	
	// A map of connection identifiers and objects
	private Map<String, Connection> connections = new ConcurrentHashMap<String, Connection>();
	// A map of callback identifiers and objects
	private Map<String, Connection.Callback<?>> callbacks = new ConcurrentHashMap<String, Connection.Callback<?>>();
	
	// GET
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// WebSocketServlet executes doWebSocketConnect method instead of doGet method if the request is a valid WebSocket request
		String transport = request.getParameter("transport");
		if (transport.equals("streamiframe") || transport.equals("streamxdr") || transport.equals("streamxhr") || transport.equals("sse")) {
			doStreamConnect(request, response, transport);
		} else if (transport.equals("longpollajax") || transport.equals("longpollxdr") || transport.equals("longpolljsonp")) {
			doLongPollConnect(request, response, transport);
		}
	}
	
	private abstract class AbstractConnection implements Connection {

		Map<String, Object> data = new ConcurrentHashMap<String, Object>();
		AtomicInteger eventId = new AtomicInteger(0);
		Timer heartbeatTimer;
		
		void setData(HttpServletRequest request) {
			data.put("request", request);
			data.put("id", request.getParameter("id"));
			data.put("transport", request.getParameter("transport"));
			data.put("heartbeat", request.getParameter("heartbeat"));
		}

		void setHeartbeat() {
			// Cancels if scheduled
			if (heartbeatTimer != null) {
				heartbeatTimer.cancel();
			}
			// Schedules the close
			heartbeatTimer = new Timer();
			heartbeatTimer.schedule(new TimerTask() {
				@Override
				public void run() {
					close();
				}
			}, (Long) data.get("heartbeat"));
		}

		@Override
		public Map<String, Object> data() {
			return data;
		}

		@Override
		public void send(Object data) {
			doSend("message", data, null);
		}

		@Override
		public void send(String event, Object data) {
			doSend(event, data, null);
		}
		
		@Override
		public void sendWithCallback(Object data, Callback<?> callback) {
			doSend("message", data, callback);
		}
		
		@Override
		public void sendWithCallback(String event, Object data, Callback<?> callback) {
			doSend(event, data, callback);
		}
		
		void doSend(String event, Object datum, Callback<?> callback) {
			// eventId++
			eventId.incrementAndGet();
			// Puts a callback with a id made of connection id and event id
			if (callback != null) {
				callbacks.put(data.get("id") + ":" + eventId, callback);
			}
			
			Map<String, Object> map = new LinkedHashMap<String, Object>();
			map.put("id", eventId.get());
			map.put("reply", callback != null);
			map.put("type", event);
			map.put("data", datum);
			
			try {
				transmit(new Gson().toJson(map));
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
	
		abstract void transmit(String data) throws IOException;
	
	}

	// POST
	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// Some Internet Explorer sends wrong encoded data for some reason
		request.setCharacterEncoding("utf-8");
		// Because the Content-Type is not application/x-www-form-urlencoded but text/plain on account of XDomainRequest,
		// You should use the POST request's message body to retrieve a message instead of request.getParameter method 
		// See the fourth at http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx
		String message = request.getReader().readLine().substring("data=".length());
		// By default, message is a JSON string representing an event
		Map<String, Object> event = new Gson().fromJson(message, new TypeToken<Map<String, Object>>() {}.getType());
		fire(event);
		// CORS
		response.setHeader("Access-Control-Allow-Origin", "*");
	}

	// Handles ws transport
	@Override
	public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) {
		final String id = request.getParameter("id");
		final String heartbeat = request.getParameter("heartbeat"); 
		final WebSocketConnection c = new WebSocketConnection();
		c.setData(request);

		return new WebSocket.OnTextMessage() {<commit_msg>Internet Explorer sometimes sends empty body<commit_after>package org.flowersinthesand.jquerysocket;

import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;

import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.WebSocketServlet;
import org.reflections.Reflections;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

@SuppressWarnings("serial")
public class DispatcherServlet extends WebSocketServlet {
	
	// A map of connection identifiers and objects
	private Map<String, Connection> connections = new ConcurrentHashMap<String, Connection>();
	// A map of callback identifiers and objects
	private Map<String, Connection.Callback<?>> callbacks = new ConcurrentHashMap<String, Connection.Callback<?>>();
	
	// GET
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// WebSocketServlet executes doWebSocketConnect method instead of doGet method if the request is a valid WebSocket request
		String transport = request.getParameter("transport");
		if (transport.equals("streamiframe") || transport.equals("streamxdr") || transport.equals("streamxhr") || transport.equals("sse")) {
			doStreamConnect(request, response, transport);
		} else if (transport.equals("longpollajax") || transport.equals("longpollxdr") || transport.equals("longpolljsonp")) {
			doLongPollConnect(request, response, transport);
		}
	}
	
	private abstract class AbstractConnection implements Connection {

		Map<String, Object> data = new ConcurrentHashMap<String, Object>();
		AtomicInteger eventId = new AtomicInteger(0);
		Timer heartbeatTimer;
		
		void setData(HttpServletRequest request) {
			data.put("request", request);
			data.put("id", request.getParameter("id"));
			data.put("transport", request.getParameter("transport"));
			data.put("heartbeat", request.getParameter("heartbeat"));
		}

		void setHeartbeat() {
			// Cancels if scheduled
			if (heartbeatTimer != null) {
				heartbeatTimer.cancel();
			}
			// Schedules the close
			heartbeatTimer = new Timer();
			heartbeatTimer.schedule(new TimerTask() {
				@Override
				public void run() {
					close();
				}
			}, (Long) data.get("heartbeat"));
		}

		@Override
		public Map<String, Object> data() {
			return data;
		}

		@Override
		public void send(Object data) {
			doSend("message", data, null);
		}

		@Override
		public void send(String event, Object data) {
			doSend(event, data, null);
		}
		
		@Override
		public void sendWithCallback(Object data, Callback<?> callback) {
			doSend("message", data, callback);
		}
		
		@Override
		public void sendWithCallback(String event, Object data, Callback<?> callback) {
			doSend(event, data, callback);
		}
		
		void doSend(String event, Object datum, Callback<?> callback) {
			// eventId++
			eventId.incrementAndGet();
			// Puts a callback with a id made of connection id and event id
			if (callback != null) {
				callbacks.put(data.get("id") + ":" + eventId, callback);
			}
			
			Map<String, Object> map = new LinkedHashMap<String, Object>();
			map.put("id", eventId.get());
			map.put("reply", callback != null);
			map.put("type", event);
			map.put("data", datum);
			
			try {
				transmit(new Gson().toJson(map));
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
	
		abstract void transmit(String data) throws IOException;
	
	}

	// POST
	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// Some Internet Explorer sends wrong encoded data for some reason
		request.setCharacterEncoding("utf-8");
		// Because the Content-Type is not application/x-www-form-urlencoded but text/plain on account of XDomainRequest,
		// You should use the POST request's message body to retrieve a message instead of request.getParameter method 
		// See the fourth at http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx
		String message = request.getReader().readLine();
		if (message != null) {
			// By default, message is a JSON string representing an event
			Map<String, Object> event = new Gson().fromJson(message.substring("data=".length()), new TypeToken<Map<String, Object>>() {}.getType());
			fire(event);
		}
		// CORS
		response.setHeader("Access-Control-Allow-Origin", "*");
	}

	// Handles ws transport
	@Override
	public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) {
		final String id = request.getParameter("id");
		final String heartbeat = request.getParameter("heartbeat"); 
		final WebSocketConnection c = new WebSocketConnection();
		c.setData(request);

		return new WebSocket.OnTextMessage() {
{'fraction_non_alphanumeric': 0.11904542522804755, 'fraction_numerical': 0.0018428084400626554, 'mean_word_length': 4.751987281399046, 'pattern_counts': {'":': 2, '<': 37, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '535882', 'n_tokens_mistral': 3423, 'n_tokens_neox': 3059, 'n_words': 810}