// Path: src/main/java/org/jmeterplugins/repository/exception/DownloadException.java
// public class DownloadException extends RuntimeException {
//     public DownloadException() {
//     }
// 
//     public DownloadException(String message) {
//         super(message);
//     }
// 
//     public DownloadException(String message, Throwable cause) {
//         super(message, cause);
//     }
// }
// 
// Path: src/main/java/org/jmeterplugins/repository/http/StatsReporter.java
// public class StatsReporter extends Thread {
//     private static final Logger log = LoggerFactory.getLogger(StatsReporter.class);
// 
//     private final JARSource jarSource;
//     private final String[] usageStats;
// 
//     public StatsReporter(JARSource jarSource, String[] usageStats) throws CloneNotSupportedException {
//         this.jarSource = (JARSource) jarSource.clone();
//         this.usageStats = usageStats;
//         setDaemon(true);
//     }
// 
//     @Override
//     public void run() {
//         try {
//             jarSource.reportStats(usageStats);
//             log.debug("Finished send repo stats");
//         } catch (IOException e) {
//             log.warn("Failed to send repo stats", e);
//         }
//     }
// 
//     protected JARSource getJarSource() {
//         return jarSource;
//     }
// }


import net.sf.json.JSON;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import net.sf.json.JSONSerializer;
import org.apache.jmeter.assertions.Assertion;
import org.apache.jmeter.config.ConfigElement;
import org.apache.jmeter.control.Controller;
import org.apache.jmeter.engine.JMeterEngine;
import org.apache.jmeter.gui.JMeterGUIComponent;
import org.apache.jmeter.processor.PostProcessor;
import org.apache.jmeter.processor.PreProcessor;
import org.apache.jmeter.samplers.SampleListener;
import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.testbeans.TestBean;
import org.apache.jmeter.timers.Timer;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.reflect.ClassFinder;
import org.jmeterplugins.repository.exception.DownloadException;
import org.jmeterplugins.repository.http.StatsReporter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.file.AccessDeniedException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
        File restartFile;
        if (doRestart) {
            restartFile = maker.getRestartFile(additionalJMeterOptions);
        } else {
            restartFile = null;
        }
        final ProcessBuilder builder = maker.getProcessBuilder(moveFile, installFile, restartFile);
        log.info("JAR Modifications log will be saved into: " + builder.redirectOutput().file().getPath());
        builder.start();
    }

    public void applyChanges(GenericCallback<String> statusChanged, boolean doRestart, LinkedList<String> additionalJMeterOptions) {
        try {
            checkRW();
        } catch (Throwable e) {
            throw new RuntimeException("Cannot apply changes: " + e.getMessage(), e);
        }

        DependencyResolver resolver = new DependencyResolver(allPlugins);
        Set<Plugin> additions = resolver.getAdditions();
        Set<Library.InstallationInfo> libInstalls = new HashSet<>();

        for (Map.Entry<String, String> entry : resolver.getLibAdditions().entrySet()) {
            try {
                JARSource.DownloadResult dwn = jarSource.getJAR(entry.getKey(), entry.getValue(), statusChanged);
                libInstalls.add(new Library.InstallationInfo(entry.getKey(), dwn.getTmpFile(), dwn.getFilename()));
            } catch (Throwable e) {
                String msg = "Failed to download " + entry.getKey();
                log.error(msg, e);
                statusChanged.notify(msg);
// Path: src/main/java/org/jmeterplugins/repository/exception/DownloadException.java
// public class DownloadException extends RuntimeException {
//     public DownloadException() {
//     }
// 
//     public DownloadException(String message) {
//         super(message);
//     }
// 
//     public DownloadException(String message, Throwable cause) {
//         super(message, cause);
//     }
// }
// 
// Path: src/main/java/org/jmeterplugins/repository/http/StatsReporter.java
// public class StatsReporter extends Thread {
//     private static final Logger log = LoggerFactory.getLogger(StatsReporter.class);
// 
//     private final JARSource jarSource;
//     private final String[] usageStats;
// 
//     public StatsReporter(JARSource jarSource, String[] usageStats) throws CloneNotSupportedException {
//         this.jarSource = (JARSource) jarSource.clone();
//         this.usageStats = usageStats;
//         setDaemon(true);
//     }
// 
//     @Override
//     public void run() {
//         try {
//             jarSource.reportStats(usageStats);
//             log.debug("Finished send repo stats");
//         } catch (IOException e) {
//             log.warn("Failed to send repo stats", e);
//         }
//     }
// 
//     protected JARSource getJarSource() {
//         return jarSource;
//     }
// }


// Path: src/main/java/org/jmeterplugins/repository/PluginManager.java
import net.sf.json.JSON;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import net.sf.json.JSONSerializer;
import org.apache.jmeter.assertions.Assertion;
import org.apache.jmeter.config.ConfigElement;
import org.apache.jmeter.control.Controller;
import org.apache.jmeter.engine.JMeterEngine;
import org.apache.jmeter.gui.JMeterGUIComponent;
import org.apache.jmeter.processor.PostProcessor;
import org.apache.jmeter.processor.PreProcessor;
import org.apache.jmeter.samplers.SampleListener;
import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.testbeans.TestBean;
import org.apache.jmeter.timers.Timer;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.reflect.ClassFinder;
import org.jmeterplugins.repository.exception.DownloadException;
import org.jmeterplugins.repository.http.StatsReporter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.file.AccessDeniedException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

        File restartFile;
        if (doRestart) {
            restartFile = maker.getRestartFile(additionalJMeterOptions);
        } else {
            restartFile = null;
        }
        final ProcessBuilder builder = maker.getProcessBuilder(moveFile, installFile, restartFile);
        log.info("JAR Modifications log will be saved into: " + builder.redirectOutput().file().getPath());
        builder.start();
    }

    public void applyChanges(GenericCallback<String> statusChanged, boolean doRestart, LinkedList<String> additionalJMeterOptions) {
        try {
            checkRW();
        } catch (Throwable e) {
            throw new RuntimeException("Cannot apply changes: " + e.getMessage(), e);
        }

        DependencyResolver resolver = new DependencyResolver(allPlugins);
        Set<Plugin> additions = resolver.getAdditions();
        Set<Library.InstallationInfo> libInstalls = new HashSet<>();

        for (Map.Entry<String, String> entry : resolver.getLibAdditions().entrySet()) {
            try {
                JARSource.DownloadResult dwn = jarSource.getJAR(entry.getKey(), entry.getValue(), statusChanged);
                libInstalls.add(new Library.InstallationInfo(entry.getKey(), dwn.getTmpFile(), dwn.getFilename()));
            } catch (Throwable e) {
                String msg = "Failed to download " + entry.getKey();
                log.error(msg, e);
                statusChanged.notify(msg);
                throw new DownloadException("Failed to download library " + entry.getKey(), e);