Last active
October 11, 2021 04:52
-
-
Save cypok/8c1a3df1f8dc1548c855403c76356103 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.sun.net.httpserver.HttpExchange; | |
import com.sun.net.httpserver.HttpHandler; | |
import com.sun.net.httpserver.HttpServer; | |
import java.io.*; | |
import java.net.InetSocketAddress; | |
import java.nio.charset.StandardCharsets; | |
import java.text.ParseException; | |
import java.text.SimpleDateFormat; | |
import java.util.*; | |
public class HearTheTennisBackend implements HttpHandler { | |
private static final String COLOR_FREE = "#b1fc99"; | |
private static final String COLOR_BUSY = "#f3d3d9"; | |
private static final String DATA_FILE = "data.csv"; | |
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); | |
private final ArrayList<Tick> ticks = new ArrayList<>(); | |
public static void main(String[] args) throws IOException, ParseException { | |
HttpHandler handler = new HearTheTennisBackend(); | |
HttpServer server = HttpServer.create(new InetSocketAddress(8008), 0); | |
server.createContext("/", handler); | |
server.createContext("/put", handler); | |
server.setExecutor(java.util.concurrent.Executors.newCachedThreadPool()); | |
server.start(); | |
} | |
public HearTheTennisBackend() throws IOException, ParseException { | |
readTicksFromDisk(); | |
writeTicksToDisk(); | |
} | |
@Override | |
public void handle(HttpExchange t) throws IOException { | |
final String responseBody; | |
final int responseCode; | |
System.out.println(t.getRequestURI().toString()); | |
switch (t.getRequestMethod()) { | |
case "GET": | |
if (!t.getRequestURI().toString().equals("/")) { | |
responseCode = 404; | |
responseBody = "unexpected request"; | |
} else { | |
responseCode = 200; | |
responseBody = renderMainPage(); | |
} | |
break; | |
case "POST": | |
if (!t.getRequestURI().toString().startsWith("/put?")) { | |
responseCode = 404; | |
responseBody = "unexpected request"; | |
} else { | |
final Map<String, String> params = queryToMap(t.getRequestURI().getQuery()); | |
final String business = params.get("business"); | |
final Tick tick = addNewTick(business); | |
responseCode = 200; | |
responseBody = tick.toString(); | |
} | |
break; | |
default: | |
responseCode = 400; | |
responseBody = "unexpected method " + t.getRequestMethod(); | |
break; | |
} | |
final OutputStream responseOS = t.getResponseBody(); | |
final byte[] responseBytes = responseBody.getBytes(StandardCharsets.US_ASCII); | |
t.sendResponseHeaders(responseCode, responseBytes.length); | |
responseOS.write(responseBytes); | |
responseOS.close(); | |
} | |
private Tick addNewTick(String business) throws IOException { | |
final State state = State.byName(business); | |
Tick tick = new Tick(new Date(), state); | |
System.out.println(tick); | |
ticks.add(tick); | |
writeTicksToDisk(); | |
return tick; | |
} | |
private String renderMainPage() { | |
final StringBuilder sb = new StringBuilder(); | |
sb.append("<html><body>Last states:<br/>"); | |
for (int i = ticks.size() - 1; i >= 0; i--) { | |
Tick tick = ticks.get(i); | |
final String color; | |
switch (tick.state) { | |
case FREE: | |
color = COLOR_FREE; | |
break; | |
case BUSY: | |
color = COLOR_BUSY; | |
break; | |
case NODATA: | |
color = null; | |
break; | |
default: | |
throw new IllegalStateException("Unexpected value: " + tick.state); | |
} | |
if (i != ticks.size() - 1) { | |
sb.append("<br/>"); | |
} | |
String s = DATE_FORMAT.format(tick.time); | |
if (color != null) { | |
sb.append("<span style='background-color:").append(color).append("'>").append(s).append("</span>"); | |
} else { | |
sb.append(s); | |
} | |
} | |
sb.append("</body></html>"); | |
return sb.toString(); | |
} | |
public static Map<String, String> queryToMap(String query) { | |
if (query == null) { | |
return null; | |
} | |
Map<String, String> result = new HashMap<>(); | |
for (String param : query.split("&")) { | |
String[] entry = param.split("="); | |
if (entry.length > 1) { | |
result.put(entry[0], entry[1]); | |
} else { | |
result.put(entry[0], ""); | |
} | |
} | |
return result; | |
} | |
private void readTicksFromDisk() throws IOException, ParseException { | |
ticks.clear(); | |
try (FileReader fr = new FileReader(DATA_FILE); | |
BufferedReader br = new BufferedReader(fr)) { | |
String line; | |
while ((line = br.readLine()) != null) { | |
String[] parts = line.split(","); | |
if (parts.length != 2) { | |
continue; | |
} | |
String date = parts[0]; | |
String state = parts[1]; | |
Tick tick = new Tick(DATE_FORMAT.parse(date), State.byName(state)); | |
ticks.add(tick); | |
} | |
} catch (FileNotFoundException e) { | |
// ignore it, we will create it | |
} | |
} | |
private void writeTicksToDisk() throws IOException { | |
try (FileWriter fw = new FileWriter(DATA_FILE); | |
BufferedWriter bw = new BufferedWriter(fw)) { | |
for (Tick tick : ticks) { | |
bw.write(DATE_FORMAT.format(tick.time) + "," + tick.state + "\n"); | |
} | |
} | |
} | |
} | |
class Tick { | |
final Date time; | |
final State state; | |
Tick(Date time, State state) { | |
this.time = time; | |
this.state = state; | |
} | |
@Override | |
public String toString() { | |
return "{" + time + ", " + state + "}"; | |
} | |
} | |
enum State { | |
NODATA, | |
FREE, | |
BUSY, | |
; | |
public static State byName(String s) { | |
return valueOf(s.toUpperCase(Locale.ROOT)); | |
} | |
public String toString() { | |
return name().toLowerCase(Locale.ROOT); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>Hear the Tennis</title> | |
<style> | |
.curstate { | |
width: 10em; | |
height: 2em; | |
line-height: 2em; | |
border: 1px; | |
text-align: center; | |
} | |
.free { | |
background-color: #b1fc99; | |
padding: 5px; | |
} | |
.busy { | |
background-color: #f3d3d9; | |
} | |
.last-update { | |
font-size: small; | |
} | |
</style> | |
</head> | |
<body> | |
<iframe src="https://giphy.com/embed/ECwTCTrHPVqKI" width="400" height="300" frameBorder="0" class="giphy-embed"></iframe> | |
<!-- <iframe src="https://giphy.com/embed/g01ZnwAUvutuK8GIQn" width="533" height="300" frameBorder="0" class="giphy-embed"></iframe>--> | |
<!-- <iframe src="https://giphy.com/embed/l0NwvUd7IEjn1764U" width="533" height="300" frameBorder="0" class="giphy-embed"></iframe>--> | |
<p> | |
Table tennis room is <span class="free">free</span> for the last 23 minutes. | |
</p> | |
<p> | |
No information for the last 48 minutes, sorry. Contact guys at 817. | |
</p> | |
<p> | |
<span class="last-update">Last update: 2021-10-11 11:18:02.</span> | |
</p> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment