All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.moskito.control.plugins.pagespeed.PagespeedPlugin Maven / Gradle / Ivy

The newest version!
package org.moskito.control.plugins.pagespeed;

import net.anotheria.moskito.aop.annotation.DontMonitor;
import net.anotheria.moskito.aop.annotation.Monitor;
import net.anotheria.moskito.core.accumulation.AccumulatorDefinition;
import net.anotheria.moskito.core.accumulation.AccumulatorRepository;
import net.anotheria.moskito.core.dynamic.OnDemandStatsProducer;
import net.anotheria.moskito.core.producers.IStatsProducer;
import net.anotheria.moskito.core.registry.IProducerRegistry;
import net.anotheria.moskito.core.registry.ProducerRegistryFactory;
import net.anotheria.moskito.core.threshold.Threshold;
import net.anotheria.moskito.core.threshold.ThresholdDefinition;
import net.anotheria.moskito.core.threshold.ThresholdRepository;
import net.anotheria.moskito.core.threshold.ThresholdStatus;
import net.anotheria.moskito.core.threshold.guard.DoubleBarrierPassGuard;
import net.anotheria.moskito.core.threshold.guard.GuardedDirection;
import net.anotheria.util.NumberUtils;
import org.moskito.control.config.datarepository.VariableMapping;
import org.moskito.control.core.Repository;
import org.moskito.control.data.DataRepository;
import org.moskito.control.data.retrievers.DataRetriever;
import org.moskito.control.plugins.AbstractMoskitoControlPlugin;
import org.moskito.control.plugins.pagespeed.stats.PagespeedStats;
import org.moskito.control.plugins.pagespeed.stats.PagespeedStatsFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
 * TODO comment this class
 *
 * @author lrosenberg
 * @since 05.08.20 17:08
 */
public class PagespeedPlugin extends AbstractMoskitoControlPlugin implements DataRetriever {

	private static Logger log = LoggerFactory.getLogger(PagespeedPlugin.class);

	/**
	 * Name of the configuration.
	 */
	private String configurationName;

	private ScheduledExecutorService scheduler;

	private ExecutorService taskExecutorService;
	/**
	 * My config. TODO later add re-load of config on @AfterConfiguration.
	 */
	PagespeedPluginConfig config = null;

	private Map> results = new HashMap<>();

	private AtomicLong executionNumberCounter = new AtomicLong();

	@Override
	public void initialize() {
		super.initialize();
		config = PagespeedPluginConfig.getByName(configurationName);
		Repository.getInstance().addCustomConfigurationProvider(new PagespeedConfigurationProvider(config));
		scheduler = Executors.newSingleThreadScheduledExecutor();
		taskExecutorService = Executors.newFixedThreadPool(10);
		//for now every 10 minutes, later 50 minutes.
		scheduler.scheduleAtFixedRate(new PagespeedPluginRunnable(),0, 15, TimeUnit.MINUTES);
		DataRepository.getInstance().addDataRetriever(this);

	}

	@Override
	public void setConfigurationName(String configurationName) {
		this.configurationName = configurationName;
	}

	@Override
	public Map retrieveData() {
		HashMap ret = new HashMap<>();
		try {
			Map> currentResults = results;
			PagespeedPluginTargetConfig[] targets = config.getTargets();
			for (PagespeedPluginTargetConfig target : targets) {
				String prefix = "pagespeed." + target.getName() + ".";
				Map resultsForTarget = currentResults.get(target.getName());
				if (resultsForTarget == null){
					log.warn("Couldn't obtain results for target "+target.getName());
					continue;
				}
				for (Map.Entry entry : resultsForTarget.entrySet()) {
					ret.put(prefix + entry.getKey(), entry.getValue());
				}
			}
		}catch(Exception any){
			log.error("Can't retrieve data completely, aborted", any);
		}
		return ret;

	}

	@Override
	public void configure(String configurationParameter, List mappings) {

	}

	List createTasks(){
		PagespeedPluginTargetConfig[] targets = config.getTargets();
		LinkedList tasks = new LinkedList<>();
		for (PagespeedPluginTargetConfig target : targets){
			PagespeedTask task = new PagespeedTask(config.getApiKey(), target);
			tasks.add(task);
		}
		return tasks;

	}

	@Monitor(producerId = "Pagespeed.PluginExecutor", subsystem = "pagespeed")
	class PagespeedPluginTaskRunnable implements Runnable{

		private PagespeedTask task;
		private CountDownLatch notifyWhenFinished;
		private Map> results;

		public PagespeedPluginTaskRunnable(PagespeedTask task, CountDownLatch latch, Map>  globalResults) {
			this.task = task;
			this.notifyWhenFinished = latch;
			this.results = globalResults;
		}


		@Override
		public void run() {
			try {
				Map taskResult = task.execute();
				results.put(task.getName(), taskResult);
				OnDemandStatsProducer producer = getProducerByName(task.getName());
				for (Map.Entry entry : taskResult.entrySet()){
					String key = entry.getKey();
					String value = entry.getValue();
					producer.getStats(key).setValue(value);
				}
			}catch(Exception e){
				log.error("Can't execute task "+task, e);
			}finally {
				notifyWhenFinished.countDown();
			}
		}
		@DontMonitor
		private OnDemandStatsProducer getProducerByName(String name){
			IProducerRegistry producerRegistry = ProducerRegistryFactory.getProducerRegistryInstance();
			String producerName = Constants.configName2ProducerName(name);
			IStatsProducer existingProducer = producerRegistry.getProducer(producerName);
			if (existingProducer!=null)
				return (OnDemandStatsProducer)existingProducer;

			//no producer exists, we have to create new (this is first run or reconfiguration).
			OnDemandStatsProducer newProducer = new OnDemandStatsProducer(
					producerName, "pagespeed", "pagespeed", new PagespeedStatsFactory()
			);
			producerRegistry.registerProducer(newProducer);

			//create accumulators.
			String[] accumulatorNames = config.getAutoAccumulators();
			log.info("Creating accumulators "+ Arrays.toString(accumulatorNames));
			if (accumulatorNames!=null && accumulatorNames.length>0){
				for (String an : accumulatorNames){
					AccumulatorDefinition def = new AccumulatorDefinition();
					def.setName(producerName+"."+Constants.getNameSubstitution(an));
					def.setProducerName(producerName);
					def.setIntervalName("15m");
					def.setStatName(an);
					def.setValueName(PagespeedStats.StatDef.VALUE.getStatName());
					AccumulatorRepository.getInstance().createAccumulator(def);
				}
			}

			//create thresholds
			String[] thresholdNames = config.getAutoThresholds();
			log.info("Creating thresholds "+Arrays.toString(thresholdNames));
			for (String tn : thresholdNames){
				ThresholdDefinition def = new ThresholdDefinition();
				def.setName(producerName+"."+Constants.getNameSubstitution(tn));
				def.setProducerName(producerName);
				def.setIntervalName("15m");
				def.setStatName(tn);
				def.setValueName(PagespeedStats.StatDef.VALUE.getStatName());
				Threshold t = ThresholdRepository.getInstance().createThreshold(def);
				setStandardGuards(t);

			}

			return newProducer;
		}

		@DontMonitor
		private void setStandardGuards(Threshold t){
			if (t.getDefinition().getStatName().equals("largestContentfulPaint")){
				t.addGuard(new DoubleBarrierPassGuard(ThresholdStatus.RED, 4000, GuardedDirection.UP));
				t.addGuard(new DoubleBarrierPassGuard(ThresholdStatus.ORANGE, 2500, GuardedDirection.UP));
				t.addGuard(new DoubleBarrierPassGuard(ThresholdStatus.GREEN, 2500, GuardedDirection.DOWN));

			}

			if (t.getDefinition().getStatName().equals("lighthouseResult.categories.performance.score")){
				t.addGuard(new DoubleBarrierPassGuard(ThresholdStatus.RED, 0.5, GuardedDirection.DOWN));
				t.addGuard(new DoubleBarrierPassGuard(ThresholdStatus.ORANGE, 0.5, GuardedDirection.UP));
				t.addGuard(new DoubleBarrierPassGuard(ThresholdStatus.YELLOW, 0.8, GuardedDirection.UP));
				t.addGuard(new DoubleBarrierPassGuard(ThresholdStatus.GREEN, 0.9, GuardedDirection.UP));

			}

			if (t.getDefinition().getStatName().equals("firstContentfulPaint")){
				t.addGuard(new DoubleBarrierPassGuard(ThresholdStatus.RED, 2500, GuardedDirection.UP));
				t.addGuard(new DoubleBarrierPassGuard(ThresholdStatus.ORANGE, 1000, GuardedDirection.UP));
				t.addGuard(new DoubleBarrierPassGuard(ThresholdStatus.GREEN, 1000, GuardedDirection.DOWN));

			}
		}


	}

	@Monitor(producerId = "Pagespeed.Plugin", subsystem = "pagespeed")
	class PagespeedPluginRunnable implements Runnable{
		@Override
		public void run() {
			HashMap> results = new HashMap<>();
			long executionNumber = executionNumberCounter.incrementAndGet();
			log.debug("Scheduled execution nr. "+executionNumber+" of pagespeed "+ NumberUtils.makeISO8601TimestampString(System.currentTimeMillis()));
			long startTime = System.currentTimeMillis();
			List tasks = createTasks();
			CountDownLatch latch = new CountDownLatch(tasks.size());
			for (PagespeedTask task : tasks) {
				PagespeedPluginTaskRunnable taskRunnable = new PagespeedPluginTaskRunnable(task, latch, results);
				taskExecutorService.submit(taskRunnable);
			}
			try {
				latch.await();
			}catch(InterruptedException e){
				log.warn("Couldn't receive results, waiting interrupted", e);
			}
			log.debug("Execution nr. "+executionNumber+" finished in "+((System.currentTimeMillis()-startTime)/1000)+" seconds ");
			log.debug("Global results: "+results);
			setResults(results);
		}

	}

	void setResults(Map> someResults){
		results = someResults;

	}

	@Override
	public String toString() {
		return "PagespeedPlugin{" +
				"configurationName='" + configurationName + '\'' +
				'}';
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy