package org.kohsuke.file_leak_detector;

import com.sun.tools.attach.VirtualMachine;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * Entry point for externally attaching agent into another local process.
 *
 * @author Kohsuke Kawaguchi
 */
public class Main {
    @Argument(index=0,metaVar="PID",usage="Process ID to activate file leak detector",required=true)
    public String pid;
    
    @Argument(index=1,metaVar="OPTSTR",usage="Packed option string of the form key1[=value1],key2[=value2],...")
    public String options;
    
    public static void main(String[] args) throws Exception {
        Main main = new Main();
        CmdLineParser p = new CmdLineParser(main);
        try {
            p.parseArgument(args);
            main.run();
        } catch (CmdLineException e) {
            System.err.println(e.getMessage());
            System.err.println("java -jar file-leak-detector.jar PID [OPTSTR]");
            p.printUsage(System.err);
            System.err.println("\nOptions:");
            AgentMain.printOptions();
            System.exit(1);
        }
    }

    public void run() throws Exception {
        Class api = loadAttachApi();

        System.out.println("Connecting to "+pid);
        Object vm = api.getMethod("attach",String.class).invoke(null,pid);

        try {
            File agentJar = whichJar(getClass());
            System.out.println("Activating file leak detector at "+agentJar);
            // load a specified agent onto the JVM
            api.getMethod("loadAgent",String.class,String.class).invoke(vm, agentJar.getPath(), options);
        } finally {
            api.getMethod("detach").invoke(vm);
        }
    }

    /**
     * Loads the {@link VirtualMachine} class as the entry point to the attach API.
     */
    private Class loadAttachApi() throws MalformedURLException, ClassNotFoundException {
        File toolsJar = locateToolsJar();
        URLClassLoader cl = new URLClassLoader(new URL[]{toolsJar.toURI().toURL()},getClass().getClassLoader());
        try {
            return cl.loadClass("com.sun.tools.attach.VirtualMachine");
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException("Unable to find tools.jar at "+toolsJar+" --- you need to run this tool with a JDK",e);
        }
    }

    /**
     * Locates the {@code tools.jar} file. Note that on Mac there's no such file but the class is still loadable.
     */
    private File locateToolsJar() {
        File home = new File(System.getProperty("java.home"));
        return new File(home,"../lib/tools.jar");
    }

    /**
     * Finds the jar file from a reference to class within.
     */
    private File whichJar(Class c) {
        String url = c.getClassLoader().getResource(c.getName().replace('.', '/') + ".class").toExternalForm();
        if (url.startsWith("jar:file:")) {
            url = url.substring(0,url.lastIndexOf('!'));
            url = url.substring(9);
            return new File(url);
        }
        throw new IllegalStateException("Unable to figure out the file of the jar: "+url);
    }
}

@@ -203,28 +203,149 @@
 er;%0A
-%0Aimport java.io.File
+import sun.tools.jar.resources.jar;%0A%0Aimport java.io.File;%0Aimport java.lang.reflect.InvocationTargetException;%0Aimport java.lang.reflect.Method
 ;%0Aim
@@ -433,16 +433,81 @@
 sLoader;
+%0Aimport java.util.logging.Level;%0Aimport java.util.logging.Logger;
 %0A%0A/**%0A *
@@ -2281,24 +2281,25 @@
 sJar();%0A
+%0A
         
 URLClass
@@ -2290,19 +2290,16 @@
         
-URL
 ClassLoa
@@ -2307,23 +2307,24 @@
 er cl = 
-new URL
+wrapInto
 ClassLoa
@@ -2331,71 +2331,16 @@
 der(
-new URL%5B%5D%7BtoolsJar.toURI().toURL()%7D,getClass().getClassLoader()
+toolsJar
 );%0A 
@@ -2610,32 +2610,1158 @@
        %7D%0A    %7D%0A%0A
+    /**%0A     * Figures out how to load tools.jar into a classloader.%0A     *%0A     * The attachment API relies on JNI, so if we have other processes in the JVM that tries to use the attach API%0A     * (like JavaMelody), it'll cause a failure. So we try to load tools.jar into the application classloadr%0A     * so that later attempts to load tools.jar will see it.%0A     */%0A    protected ClassLoader wrapIntoClassLoader(File toolsJar) throws MalformedURLException %7B%0A        URL jar = toolsJar.toURI().toURL();%0A        %0A        ClassLoader base = getClass().getClassLoader();%0A        if (base instanceof URLClassLoader) %7B%0A            try %7B%0A                Method addURL = URLClassLoader.class.getDeclaredMethod(%22addURL%22, URL.class);%0A                addURL.setAccessible(true);%0A                addURL.invoke(base,jar);%0A                return base;%0A            %7D catch (Exception e) %7B%0A                // if that fails, load into a separate classloader%0A                LOGGER.log(Level.WARNING, %22Failed to load tools.jar into appclassloader%22,e);%0A            %7D%0A        %7D%0A%0A        return new URLClassLoader(new URL%5B%5D%7Bjar%7D, base);%0A    %7D%0A%0A
     /**%0A     * L
@@ -4531,11 +4531,93 @@
 );%0A    %7D
+%0A%0A    private static final Logger LOGGER = Logger.getLogger(Main.class.getName());
 %0A%7D%0A

load into self