package com.youdevise.hudson.slavestatus;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Serializable;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import hudson.remoting.Callable;
import static com.youdevise.hudson.slavestatus.Daemon.RunType;
import static com.youdevise.hudson.slavestatus.Daemon.RunResult;
public class SlaveListener implements Callable<Object, Throwable>, Serializable {
private static final long serialVersionUID = 1L;
private static final String HTTP_HEADERS
= "HTTP/1.0 200 OK\n"
+ "Content-Type: text/xml\n"
+ "Server: Hudson slave-status plugin\n"
+ "\n";
private final int port;
private final RunType runType;
private final List<StatusReporter> reporters = new ArrayList<StatusReporter>();
private transient HTTPListener httpListener;
private transient Logger logger;
public SlaveListener(int port, StatusReporter ...reporters) throws IOException {
this(port, RunType.FOREVER, reporters);
}
public SlaveListener(int port, RunType runType, StatusReporter ...reporters) {
this.port = port;
this.runType = runType;
this.reporters.addAll(Arrays.asList(reporters));
}
public void setLogger(Logger logger) {
this.logger = logger;
}
public void setHTTPListener(HTTPListener httpListener) {
this.httpListener = httpListener;
}
public Object call() throws Throwable {
if (null == logger) { logger = Logger.getLogger(this.getClass().getName()); }
if (null == httpListener) { httpListener = new SocketHTTPListener(port, logger); }
logger.info("Slave-status listener starting");
Daemon daemon = new Daemon(new DaemonRunner() {
public RunResult run() {
logger.fine("Slave-status listener waiting for connection");
try {
httpListener.waitForConnection();
} catch (IOException e) {
logger.log(Level.SEVERE, "Could not listen on port", e);
return RunResult.ABORT;
}
try {
logger.fine("Slave-status listener got connection");
readAndIgnoreInput(httpListener);
logger.fine("Slave-status listener read input");
httpListener.getOutputStream().write(getOutput());
logger.fine("Slave-status listener wrote output");
httpListener.flushAndClose();
logger.fine("Slave-status listener flushed and closed connection");
} catch (IOException e) {
logger.log(Level.SEVERE, "Exception when handling request", e);
}
return RunResult.CONTINUE;
}
});
return daemon.go(runType);
}
private byte[] getOutput() {
StringBuffer xml = new StringBuffer();
xml.append("<slave>");
for (StatusReporter reporter : reporters) {
xml.append(String.format("<%s>%s</%s>", reporter.getName(), reporter.getContent(), reporter.getName()));
}
xml.append("</slave>");
return (HTTP_HEADERS + xml).getBytes();
}
private void readAndIgnoreInput(HTTPListener httpListener) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(httpListener.getInputStream()));
String line;
do {
line = reader.readLine();
} while (line.length() > 0);
}
public int getPort() { return port; }
public List<StatusReporter> getReporters() { return reporters; }
}
interface HTTPListener {
void waitForConnection() throws IOException;
InputStream getInputStream() throws IOException;
OutputStream getOutputStream() throws IOException;
void flushAndClose() throws IOException;
}
class SocketHTTPListener implements HTTPListener {
private final int port;
private final Logger logger;
private ServerSocket serverSocket = null;
private Socket socket = null;
public SocketHTTPListener(int port, Logger logger) {
this.port = port;
this.logger = logger;
}
public void waitForConnection() throws IOException {
if (null == serverSocket) {
serverSocket = new ServerSocket(port);
logger.info("Slave-status listener ready on port " + port);
}
socket = serverSocket.accept();
}
public InputStream getInputStream() throws IOException { return socket.getInputStream(); }
public OutputStream getOutputStream() throws IOException { return socket.getOutputStream(); }
public void flushAndClose() throws IOException {
if (null != socket) {
socket.getOutputStream().flush();
socket.close();
}
}
}