| 
1738
 | 
     1 package goodjava.webserver;
 | 
| 
 | 
     2 
 | 
| 
 | 
     3 import java.io.Writer;
 | 
| 
 | 
     4 import java.io.OutputStreamWriter;
 | 
| 
 | 
     5 import java.io.BufferedWriter;
 | 
| 
 | 
     6 import java.io.IOException;
 | 
| 
 | 
     7 import java.net.Socket;
 | 
| 
 | 
     8 import java.util.List;
 | 
| 
 | 
     9 import java.util.ArrayList;
 | 
| 
 | 
    10 import java.util.Map;
 | 
| 
 | 
    11 import java.util.HashMap;
 | 
| 
 | 
    12 import java.util.Collections;
 | 
| 
 | 
    13 import java.util.Iterator;
 | 
| 
 | 
    14 
 | 
| 
 | 
    15 
 | 
| 
 | 
    16 public class ServerSentEvents {
 | 
| 
 | 
    17 
 | 
| 
 | 
    18 	private static class Con {
 | 
| 
 | 
    19 		final Socket socket;
 | 
| 
 | 
    20 		final Writer writer;
 | 
| 
 | 
    21 
 | 
| 
 | 
    22 		Con(Socket socket) throws IOException {
 | 
| 
 | 
    23 			this.socket = socket;
 | 
| 
 | 
    24 			this.writer = new BufferedWriter( new OutputStreamWriter( socket.getOutputStream(), "UTF-8" ) );
 | 
| 
 | 
    25 		}
 | 
| 
 | 
    26 	}
 | 
| 
 | 
    27 
 | 
| 
 | 
    28 	private static class EventHandler {
 | 
| 
 | 
    29 		private final List<Con> cons = new ArrayList<Con>();
 | 
| 
 | 
    30 
 | 
| 
 | 
    31 		synchronized void add(Con con) {
 | 
| 
 | 
    32 			cons.add(con);
 | 
| 
 | 
    33 		}
 | 
| 
 | 
    34 
 | 
| 
 | 
    35 		synchronized void write( String url, String content ) {
 | 
| 
 | 
    36 			Iterator<Con> iter = cons.iterator();
 | 
| 
 | 
    37 			while( iter.hasNext() ) {
 | 
| 
 | 
    38 				Con con = iter.next();
 | 
| 
 | 
    39 				Writer writer = con.writer;
 | 
| 
 | 
    40 				try {
 | 
| 
 | 
    41 					writer.write(content);
 | 
| 
 | 
    42 					writer.write("\n\n");
 | 
| 
 | 
    43 					writer.flush();
 | 
| 
 | 
    44 				} catch(IOException e) {
 | 
| 
 | 
    45 					iter.remove();
 | 
| 
 | 
    46 				}
 | 
| 
 | 
    47 			}
 | 
| 
 | 
    48 			if( cons.isEmpty() )
 | 
| 
 | 
    49 				map.remove(url);
 | 
| 
 | 
    50 		}
 | 
| 
 | 
    51 	}
 | 
| 
 | 
    52 
 | 
| 
 | 
    53 	private static final Map<String,EventHandler> map
 | 
| 
 | 
    54 		= Collections.synchronizedMap(new HashMap<String,EventHandler>());
 | 
| 
 | 
    55 
 | 
| 
 | 
    56 	static void add(Socket socket,Request request) throws IOException {
 | 
| 
 | 
    57 		Con con = new Con(socket);
 | 
| 
 | 
    58 
 | 
| 
 | 
    59 		Writer writer = con.writer;
 | 
| 
 | 
    60 		writer.write("HTTP/1.1 200 OK\r\n");
 | 
| 
 | 
    61 		writer.write("Access-Control-Allow-Origin: *\r\n");
 | 
| 
 | 
    62 		writer.write("Cache-Control: no-cache\r\n");
 | 
| 
 | 
    63 		writer.write("Content-Type: text/event-stream\r\n");
 | 
| 
 | 
    64 		writer.write("\r\n");
 | 
| 
 | 
    65 		writer.flush();
 | 
| 
 | 
    66 
 | 
| 
 | 
    67 		String url = request.url();
 | 
| 
 | 
    68 		EventHandler handler;
 | 
| 
 | 
    69 		synchronized(map) {
 | 
| 
 | 
    70 			handler = map.get(url);
 | 
| 
 | 
    71 			if( handler==null ) {
 | 
| 
 | 
    72 				handler = new EventHandler();
 | 
| 
 | 
    73 				map.put(url,handler);
 | 
| 
 | 
    74 			}
 | 
| 
 | 
    75 		}
 | 
| 
 | 
    76 		handler.add(con);
 | 
| 
 | 
    77 	}
 | 
| 
 | 
    78 
 | 
| 
 | 
    79 	public static void write( String url, String content ) {
 | 
| 
 | 
    80 		EventHandler handler = map.get(url);
 | 
| 
 | 
    81 		if( handler != null )
 | 
| 
 | 
    82 			handler.write(url,content);
 | 
| 
 | 
    83 	}
 | 
| 
 | 
    84 
 | 
| 
 | 
    85 	public static String toData(String message) {
 | 
| 
 | 
    86 		if( message.endsWith("\n") )
 | 
| 
 | 
    87 			message = message.substring( 0, message.length() - 1 );
 | 
| 
 | 
    88 		return "data: " + message.replace( "\n", "\ndata: " ) + "\n";
 | 
| 
 | 
    89 	}
 | 
| 
 | 
    90 
 | 
| 
 | 
    91 	public static void writeMessage( String url, String message ) {
 | 
| 
 | 
    92 		write( url, toData(message) );
 | 
| 
 | 
    93 	}
 | 
| 
 | 
    94 
 | 
| 
 | 
    95 	private ServerSentEvents() {}  // never
 | 
| 
 | 
    96 }
 |