001package org.nuxeo.runtime.javaagent;
002
003import java.io.File;
004import java.io.FilenameFilter;
005import java.io.IOException;
006import java.lang.management.ManagementFactory;
007import java.lang.reflect.Field;
008import java.lang.reflect.InvocationTargetException;
009import java.net.MalformedURLException;
010import java.net.URISyntaxException;
011import java.net.URL;
012import java.nio.file.Paths;
013
014import org.nuxeo.runtime.api.Framework;
015
016import com.sun.tools.attach.VirtualMachine;
017
018public class AgentLoader {
019
020    public static final AgentLoader INSTANCE = new AgentLoader();
021
022    protected Object agent = load();
023
024    protected ObjectSizer sizer = AgentHandler.newHandler(ObjectSizer.class, agent);
025
026    public ObjectSizer getSizer() {
027        return sizer;
028    }
029
030    protected <I> I getAgent(Class<I> type) {
031        return AgentHandler.newHandler(type, agent);
032    }
033
034    protected Object load() {
035        try {
036            loadAgentIfNeeded();
037        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException
038                | InvocationTargetException | MalformedURLException | URISyntaxException cause) {
039            throw new RuntimeException("Cannot load nuxeo agent jar in virtual machine", cause);
040        }
041        try {
042            return loadInstance();
043        } catch (ClassNotFoundException | NoSuchFieldException | SecurityException | IllegalArgumentException
044                | IllegalAccessException cause) {
045            throw new RuntimeException("Cannot load nuxeo agent", cause);
046        }
047    }
048
049    protected Object loadInstance() throws ClassNotFoundException, NoSuchFieldException, SecurityException,
050            IllegalArgumentException, IllegalAccessException {
051        Class<?> clazz = AgentLoader.class.getClassLoader().loadClass("org.nuxeo.runtime.javaagent.NuxeoAgent");
052        Field agentField = clazz.getDeclaredField("agent");
053        agentField.setAccessible(true);
054        return agentField.get(null);
055    }
056
057    protected void loadAgentIfNeeded() throws NoSuchMethodException, SecurityException, IllegalAccessException,
058            IllegalArgumentException, InvocationTargetException, MalformedURLException, URISyntaxException {
059        try {
060            AgentLoader.class.getClassLoader().loadClass("org.nuxeo.runtime.javaagent.NuxeoAgent");
061        } catch (ClassNotFoundException e) {
062            loadAgent();
063        }
064    }
065
066    protected void loadAgent() throws NoSuchMethodException, SecurityException, IllegalAccessException,
067            IllegalArgumentException, InvocationTargetException, MalformedURLException, URISyntaxException {
068        String nameOfRunningVM = ManagementFactory.getRuntimeMXBean().getName();
069        int p = nameOfRunningVM.indexOf('@');
070        String pid = nameOfRunningVM.substring(0, p);
071
072        File home;
073        try {
074            home = Framework.getRuntime().getHome().getCanonicalFile();
075        } catch (IOException cause) {
076            throw new RuntimeException("cannot normalize runtime home path", cause);
077        }
078        File jarParentFolder = new File(home.getParentFile(), "bin");
079        String jarLocation = locateAgentJar(jarParentFolder);
080
081        if (jarLocation == null) {
082            boolean isUnderTest = Boolean.parseBoolean(Framework.getProperty("org.nuxeo.runtime.testing", "false"));
083            if (!isUnderTest) {
084                throw new RuntimeException("Cannot locate nuxeo agent jar in bin folder");
085            }
086            URL testResource = AgentLoader.class.getResource("/");
087            File testClasses = Paths.get(testResource.toURI()).toFile();
088            File mainProject = new File(testClasses.getParentFile().getParentFile().getParentFile(), "main");
089            File target = new File(mainProject, "target");
090            jarLocation = locateAgentJar(target);
091            if (jarLocation == null) {
092                throw new RuntimeException("Cannot locate nuxeo agent jar in target folder " + target
093                        + ", did you forgot to run package target");
094            }
095        }
096        try {
097            VirtualMachine vm = VirtualMachine.attach(pid);
098            vm.loadAgent(jarLocation, "");
099            vm.detach();
100        } catch (Exception e) {
101            throw new RuntimeException(e);
102        }
103    }
104
105    protected String locateAgentJar(File dir) {
106        File[] jars = dir.listFiles(new FilenameFilter() {
107
108            @Override
109            public boolean accept(File dir, String name) {
110                return name.startsWith("nuxeo-javaagent-main") && name.endsWith(".jar");
111            }
112        });
113        if (jars.length == 0) {
114            return null;
115        }
116        return jars[0].getPath();
117    }
118}