001/*
002 * (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Stephane Lacoin
018 */
019package org.nuxeo.runtime.javaagent;
020
021import java.io.File;
022import java.io.FilenameFilter;
023import java.io.IOException;
024import java.lang.management.ManagementFactory;
025import java.lang.reflect.Field;
026import java.lang.reflect.InvocationTargetException;
027import java.net.MalformedURLException;
028import java.net.URISyntaxException;
029import java.net.URL;
030import java.nio.file.Paths;
031
032import org.nuxeo.runtime.api.Framework;
033
034import com.sun.tools.attach.VirtualMachine;
035
036public class AgentLoader {
037
038    public static final AgentLoader INSTANCE = new AgentLoader();
039
040    protected Object agent = load();
041
042    protected ObjectSizer sizer = AgentHandler.newHandler(ObjectSizer.class, agent);
043
044    public ObjectSizer getSizer() {
045        return sizer;
046    }
047
048    protected <I> I getAgent(Class<I> type) {
049        return AgentHandler.newHandler(type, agent);
050    }
051
052    protected Object load() {
053        try {
054            loadAgentIfNeeded();
055        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException
056                | InvocationTargetException | MalformedURLException | URISyntaxException cause) {
057            throw new RuntimeException("Cannot load nuxeo agent jar in virtual machine", cause);
058        }
059        try {
060            return loadInstance();
061        } catch (ClassNotFoundException | NoSuchFieldException | SecurityException | IllegalArgumentException
062                | IllegalAccessException cause) {
063            throw new RuntimeException("Cannot load nuxeo agent", cause);
064        }
065    }
066
067    protected Object loadInstance() throws ClassNotFoundException, NoSuchFieldException, SecurityException,
068            IllegalArgumentException, IllegalAccessException {
069        Class<?> clazz = AgentLoader.class.getClassLoader().loadClass("org.nuxeo.runtime.javaagent.NuxeoAgent");
070        Field agentField = clazz.getDeclaredField("agent");
071        agentField.setAccessible(true);
072        return agentField.get(null);
073    }
074
075    protected void loadAgentIfNeeded() throws NoSuchMethodException, SecurityException, IllegalAccessException,
076            IllegalArgumentException, InvocationTargetException, MalformedURLException, URISyntaxException {
077        try {
078            AgentLoader.class.getClassLoader().loadClass("org.nuxeo.runtime.javaagent.NuxeoAgent");
079        } catch (ClassNotFoundException e) {
080            loadAgent();
081        }
082    }
083
084    protected void loadAgent() throws NoSuchMethodException, SecurityException, IllegalAccessException,
085            IllegalArgumentException, InvocationTargetException, MalformedURLException, URISyntaxException {
086        String nameOfRunningVM = ManagementFactory.getRuntimeMXBean().getName();
087        int p = nameOfRunningVM.indexOf('@');
088        String pid = nameOfRunningVM.substring(0, p);
089
090        File home;
091        try {
092            home = Framework.getRuntime().getHome().getCanonicalFile();
093        } catch (IOException cause) {
094            throw new RuntimeException("cannot normalize runtime home path", cause);
095        }
096        File jarParentFolder = new File(home.getParentFile(), "bin");
097        String jarLocation = locateAgentJar(jarParentFolder);
098
099        if (jarLocation == null) {
100            boolean isUnderTest = Boolean.parseBoolean(Framework.getProperty("org.nuxeo.runtime.testing", "false"));
101            if (!isUnderTest) {
102                throw new RuntimeException("Cannot locate nuxeo agent jar in bin folder");
103            }
104            URL testResource = AgentLoader.class.getResource("/");
105            File testClasses = Paths.get(testResource.toURI()).toFile();
106            File mainProject = new File(testClasses.getParentFile().getParentFile().getParentFile(), "main");
107            File target = new File(mainProject, "target");
108            jarLocation = locateAgentJar(target);
109            if (jarLocation == null) {
110                throw new RuntimeException("Cannot locate nuxeo agent jar in target folder " + target
111                        + ", did you forgot to run package target");
112            }
113        }
114        try {
115            VirtualMachine vm = VirtualMachine.attach(pid);
116            vm.loadAgent(jarLocation, "");
117            vm.detach();
118        } catch (Exception e) {
119            throw new RuntimeException(e);
120        }
121    }
122
123    protected String locateAgentJar(File dir) {
124        File[] jars = dir.listFiles(new FilenameFilter() {
125
126            @Override
127            public boolean accept(File dir, String name) {
128                return name.startsWith("nuxeo-javaagent-main") && name.endsWith(".jar");
129            }
130        });
131        if (jars.length == 0) {
132            return null;
133        }
134        return jars[0].getPath();
135    }
136}