I used the official example Socket server to start up a Socket server for my jacocoagent.jar to connect to. I start up my target jar as such:
java -javaagent:jacocoagent.jar=dumponexit=false,output=tcpclient,address=localhost,port=6300 -jar demo-0.0.1-SNAPSHOT.jar
I modified the Server code to add a method to capture the dump. Below is the full code:
public class JacocoSocketServer {
private static final String DESTFILE = "jacoco-server.exec";
private static final String ADDRESS = "localhost";
private static final int PORT = 6300;
private static final Logger LOGGER = Logger.getLogger(JacocoSocketServer.class.getName());
/**
* Start the server as a standalone program.
*
* @param args
* @throws IOException
*/
public static void main(final String[] args) throws IOException {
LOGGER.log(Level.INFO, "Output file is " + new File(DESTFILE).getAbsolutePath());
final RemoteControlWriter fileWriter = new RemoteControlWriter(new FileOutputStream(DESTFILE));
LOGGER.log(Level.INFO, "Starting TCP server on: " + ADDRESS + ":" + PORT);
final ServerSocket server = new ServerSocket(PORT, 0, InetAddress.getByName(ADDRESS));
final List<Handler> listHandler = new ArrayList<>();
Runnable runDumpTimer = () -> {
while (true) {
Iterator<Handler> it = listHandler.iterator();
while (it.hasNext()) {
Handler handler = it.next();
boolean toRemove = false;
String id = handler.getId();
try {
if (!handler.isAlive()) {
LOGGER.log(Level.INFO, String.format("Socket closed, removing handler: %s", id));
toRemove = true;
} else {
handler.captureDump(true, true);
}
} catch (IOException e) {
LOGGER.log(Level.INFO, "Socket error: " + e.getMessage() + ", removing handler: " + id);
toRemove = true;
} finally {
if (toRemove) {
it.remove();
}
}
}
}
};
Thread threadDumpTimer = new Thread(runDumpTimer, "threadDumpTimer");
threadDumpTimer.start();
while (true) {
Socket socket = server.accept();
System.out.println("Remote connection detected, openning socket on local port: "
+ socket.getLocalPort());
final Handler handler = new Handler(socket, fileWriter);
listHandler.add(handler);
new Thread(handler).start();
}
}
private static class Handler
implements
Runnable,
ISessionInfoVisitor,
IExecutionDataVisitor {
private final Socket socket;
private final RemoteControlReader reader;
private final RemoteControlWriter fileWriter;
private final RemoteControlWriter remoteWriter;
private String id;
public String getId() {
return id;
}
Handler(final Socket socket, final RemoteControlWriter fileWriter) throws IOException {
this.socket = socket;
this.fileWriter = fileWriter;
// Just send a valid header:
remoteWriter = new RemoteControlWriter(socket.getOutputStream());
reader = new RemoteControlReader(socket.getInputStream());
reader.setSessionInfoVisitor(this);
reader.setExecutionDataVisitor(this);
}
public void run() {
try {
while (reader.read()) {
}
socket.close();
synchronized (fileWriter) {
fileWriter.flush();
}
} catch (final IOException e) {
e.printStackTrace();
}
}
public void visitSessionInfo(final SessionInfo info) {
id = info.getId();
LOGGER.log(Level.INFO, String.format("Retrieving execution Data for session: %s", info.getId()));
synchronized (fileWriter) {
fileWriter.visitSessionInfo(info);
}
}
public void visitClassExecution(final ExecutionData data) {
synchronized (fileWriter) {
fileWriter.visitClassExecution(data);
}
}
void captureDump(boolean dump, boolean reset) throws IOException {
remoteWriter.visitDumpCommand(dump, reset);
}
boolean isAlive() {
return socket != null
&& socket.isConnected();
}
}
}
The above code will infinitely keep writing dumps to the specified file, which is obviously not the expected outcome.
Once I start the start the server, I then startup the target jar, I will run some functional test cases on the jar. Once these test cases have completed I want to trigger an execution data dump and reset the store so that the next time I trigger the dump, it will only have data about the relevant session.
PS: I used to run the server periodically to take dumps at specified intervals of time (hence some of the variable names), but that is no longer desired.
PPS: I have read the following questions, all of which had similar requirements to mine, but I could not figure out a way to remotely dump the data on command:
https://groups.google.com/forum/#!msg/jacoco/MUTIM5yS6ZQ/KSA5Og4xBgAJ
https://sourceforge.net/p/eclemma/discussion/614870/thread/083acdaa/
https://groups.google.com/forum/#!topic/jacoco/QAmXUZjviPk
https://groups.google.com/forum/#!msg/jacoco/V7Bmz5r0P24/PdCC5apIoqgJ