'Remotely triggering a jacoco execution data dump using Jacoco Agent
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
Solution 1:[1]
I haven't done it yet. But from what I've been reading I want to do exactly what you are trying to do. From what I have read, you need to start the jacoco agent in tcpserver mode and then have a remote client initiate the dump. I found this client code online that may work: https://github.com/Wageesha95/TCP_Client_JacocoCC/blob/master/src/main/java/client/ExecutionDataClient.java
Here is the docs that describe the different output modes: https://www.eclemma.org/jacoco/trunk/doc/agent.html
tcpserver: The agent listens for incoming connections on the TCP port specified by the address and port attribute. Execution data is written to this TCP connection.
So in tcpserver mode (not tcpclient that you have setup) you start the app, run your tests, then connect to the tcp server and initiate the dump.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | PBMax |
