kg.apc.jmeter.reporters.LoadosophiaConsolidator Maven / Gradle / Ivy
package kg.apc.jmeter.reporters;
import kg.apc.jmeter.JMeterPluginsUtils;
import kg.apc.jmeter.perfmon.PerfMonCollector;
import org.apache.jmeter.engine.util.NoThreadClone;
import org.apache.jmeter.reporters.ResultCollector;
import org.apache.jmeter.samplers.Remoteable;
import org.apache.jmeter.samplers.SampleEvent;
import org.apache.jmeter.samplers.SampleSaveConfiguration;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;
import org.loadosophia.jmeter.LoadosophiaAPIClient;
import org.loadosophia.jmeter.LoadosophiaUploadResults;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
public class LoadosophiaConsolidator extends ResultCollector
implements Serializable, Remoteable, NoThreadClone, Runnable {
private static final Logger log = LoggingManager.getLoggerForClass();
private static final Object LOCK = new Object();
private static LoadosophiaConsolidator instance;
private Set sources = new HashSet<>();
private boolean isSaving;
private String fileName;
private String address;
private boolean isOnlineInitiated = false;
private LoadosophiaAPIClient apiClient;
private BlockingQueue processingQueue;
private Thread processorThread;
private LoadosophiaAggregator aggregator;
protected LoadosophiaConsolidator() {
super();
address = JMeterUtils.getPropDefault("sense.address", "https://sense.blazemeter.com/");
}
public static LoadosophiaConsolidator getInstance() {
if (instance == null) {
instance = new LoadosophiaConsolidator();
}
return instance;
}
public void add(LoadosophiaUploader source) {
log.debug("Add to consolidator: " + source);
sources.add(source);
if (sources.size() == 1) {
log.debug("First source arrived, let's start the process");
start(source);
testStarted();
}
if (isOnlineInitiated) {
aggregator.setNumSources(sources.size());
}
}
public void remove(LoadosophiaUploader source) {
log.debug("Remove from consolidator: " + source);
sources.remove(source);
if (isOnlineInitiated) {
aggregator.setNumSources(getNumSources());
}
if (sources.size() == 0) {
log.debug("Last source departed, let's finish the process");
testEnded();
stop(source);
}
}
protected void start(LoadosophiaUploader source) {
synchronized (LOCK) {
this.apiClient = getAPIClient(source);
try {
if (!isSaving) {
isSaving = true;
setupSaving(source);
}
} catch (IOException ex) {
log.error("Error setting up saving", ex);
}
initiateOnline(source);
}
}
protected void stop(LoadosophiaUploader source) {
String redirectLink = "";
synchronized (LOCK) {
flush();
try {
if (fileName == null) {
throw new IOException("File for upload was not set, search for errors above in log");
}
isSaving = false;
LinkedList monFiles;
if (hasStandardSet()) {
monFiles = PerfMonCollector.getFiles();
} else {
monFiles = new LinkedList<>();
}
LoadosophiaUploadResults uploadResult = this.apiClient.sendFiles(new File(fileName), monFiles);
redirectLink = uploadResult.getRedirectLink();
source.informUser("Uploaded successfully, go to results: " + redirectLink + "
");
} catch (Throwable ex) {
source.informUser("Failed to upload results to BM.Sense, see log for detais: " + ex.getMessage());
log.error("Failed to upload results to BM.Sense", ex);
}
}
if (isOnlineInitiated) {
finishOnline(redirectLink + "#tab=tabTimelines");
}
clearData();
if (hasStandardSet()) {
PerfMonCollector.clearFiles();
}
}
private void flush() {
// FIXME: trying to handle safe upgrade, needs to be removed in the future
// @see https://issues.apache.org/bugzilla/show_bug.cgi?id=56807
try {
Class c = ResultCollector.class;
Method m = c.getDeclaredMethod("flushFile");
m.invoke(this);
log.info("Successfully flushed results file");
} catch (NoSuchMethodException ex) {
log.warn("Cannot flush results file since you are using old version of JMeter, consider upgrading to latest. Currently the results may be incomplete.");
} catch (InvocationTargetException | IllegalAccessException e) {
log.error("Failed to flush file", e);
}
}
private boolean hasStandardSet() {
boolean hasStandardSet = true;
try {
Class.forName("kg.apc.jmeter.perfmon.PerfMonCollector");
} catch (ClassNotFoundException e) {
hasStandardSet = false;
}
return hasStandardSet;
}
private void setupSaving(LoadosophiaUploader source) throws IOException {
log.debug("Set up saving with " + source);
String dir = source.getStoreDir();
File tmpFile;
try {
if (dir == null || dir.trim().isEmpty()) {
tmpFile = File.createTempFile("Sense_", ".jtl");
} else {
File storeDir = new File(dir);
storeDir.mkdirs();
tmpFile = File.createTempFile("Sense_", ".jtl", storeDir);
}
} catch (IOException ex) {
source.informUser("Unable to create temp file: " + ex.getMessage());
source.informUser("Try to set another directory in the above field.");
throw ex;
}
fileName = tmpFile.getAbsolutePath();
tmpFile.delete(); // IMPORTANT! this is required to have CSV headers
source.informUser("Storing results for upload to Sense: " + fileName);
setFilename(fileName);
// OMG, I spent 2 days finding that setting properties in testStarted
// marks them temporary, though they cleared in some places.
// So we do dirty(?) hack here...
clearTemporary(getProperty(ResultCollector.FILENAME));
SampleSaveConfiguration conf = getSaveConfig();
JMeterPluginsUtils.doBestCSVSetup(conf);
setSaveConfig(conf);
}
@Override
public void sampleOccurred(SampleEvent event) {
super.sampleOccurred(event);
if (isOnlineInitiated) {
try {
if (!processingQueue.offer(event, 1, TimeUnit.SECONDS)) {
log.warn("Failed first dequeue insert try, retrying");
if (!processingQueue.offer(event, 1, TimeUnit.SECONDS)) {
log.error("Failed second try to inser into deque, dropped sample");
}
}
} catch (InterruptedException ex) {
log.info("Interrupted while putting sample event into deque", ex);
}
}
}
@Override
public void run() {
while (isOnlineInitiated) {
try {
SampleEvent event = processingQueue.poll(1, TimeUnit.SECONDS);
if (event != null) {
aggregator.addSample(event);
}
if (aggregator.haveDataToSend()) {
try {
apiClient.sendOnlineData(aggregator.getDataToSend());
} catch (IOException ex) {
log.warn("Failed to send active test data", ex);
}
}
} catch (InterruptedException ex) {
log.debug("Interrupted while taking sample event from deque", ex);
break;
}
}
}
private void initiateOnline(LoadosophiaUploader source) {
if (source.isUseOnline()) {
try {
log.info("Starting BM.Sense online test");
String url = apiClient.startOnline();
source.informUser("Started active test: " + url + "
");
aggregator = new LoadosophiaAggregator();
processingQueue = new LinkedBlockingQueue<>();
processorThread = new Thread(this);
processorThread.setDaemon(true);
isOnlineInitiated = true;
processorThread.start();
} catch (IOException ex) {
source.informUser("Failed to start active test");
log.warn("Failed to initiate active test", ex);
this.isOnlineInitiated = false;
}
}
}
private void finishOnline(String redirectLink) {
processorThread.interrupt();
while (processorThread.isAlive() && !processorThread.isInterrupted()) {
log.info("Waiting for aggregator thread to stop...");
try {
Thread.sleep(50);
processorThread.interrupt();
} catch (InterruptedException ex) {
log.warn("Interrupted sleep", ex);
}
}
log.info("Ending BM.Sense online test");
try {
apiClient.endOnline(redirectLink);
} catch (IOException ex) {
log.warn("Failed to finalize active test", ex);
}
isOnlineInitiated = false;
}
protected LoadosophiaAPIClient getAPIClient(LoadosophiaUploader source) {
return new LoadosophiaAPIClient(source, address, source.getUploadToken(), source.getProject(), source.getColorFlag(), source.getTitle());
}
public int getNumSources() {
return sources.size();
}
public static void destroy() {
instance = null;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy