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

org.jolokia.jvmagent.client.util.VirtualMachineHandler Maven / Gradle / Ivy

There is a newer version: 1.7.2
Show newest version
package org.jolokia.jvmagent.client.util;

/*
 * Copyright 2009-2013 Roland Huss
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.lang.management.ManagementFactory;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * A handler for dealing with VirtualMachine without directly referencing internally
 * the class type. All lookup is done via reflection.
 *
 * @author roland
 * @since 12.08.11
 */
public class VirtualMachineHandler {

    private OptionsAndArgs options;

    /**
     * Constructor with options
     *
     * @param pOptions options for getting e.g. the process id to attach to
     *
     */
    public VirtualMachineHandler(OptionsAndArgs pOptions) {
        options = pOptions;
    }

    /**
     * Lookup and create a {@link com.sun.tools.attach.VirtualMachine} via reflection. First, a direct
     * lookup via {@link Class#forName(String)} is done, which will succeed for JVM on OS X, since tools.jar
     * is bundled there together with classes.zip. Next, tools.jar is tried to be found (by examine java.home)
     * and an own classloader is created for looking up the VirtualMachine.
     *
     * If lookup fails, a message is printed out (except when '--quiet' is provided)
     *
     * @return the create virtual machine of null if none could be created
     */
    public Object attachVirtualMachine() {
        if (options.getPid() == null && options.getProcessPattern() == null) {
            return null;
        }
        Class vmClass = lookupVirtualMachineClass();
        try {
            Method method = vmClass.getMethod("attach",String.class);
            return method.invoke(null, getProcessId(options));
        } catch (NoSuchMethodException e) {
            throw new ProcessingException("Internal: No method 'attach' found on " + vmClass,e,options);
        } catch (InvocationTargetException e) {
            throw new ProcessingException("InvocationTarget " + vmClass,e,options);
        } catch (IllegalAccessException e) {
            throw new ProcessingException("IllegalAccess to " + vmClass,e,options);
        } catch (IllegalArgumentException e) {
            throw new ProcessingException("Illegal Argument",e,options);
        }
    }


    /**
     * Detach from the virtual machine
     *
     * @param pVm the virtual machine to detach from
     */
    public void detachAgent(Object pVm) {
        try {
            if (pVm != null) {
                Class clazz = pVm.getClass();
                Method method = clazz.getMethod("detach");
                method.invoke(pVm);
            }
        } catch (InvocationTargetException e) {
            throw new ProcessingException("Error while detaching",e, options);
        } catch (NoSuchMethodException e) {
            throw new ProcessingException("Error while detaching",e, options);
        } catch (IllegalAccessException e) {
            throw new ProcessingException("Error while detaching",e, options);
        }
    }

    /**
     * Return a list of all Java processes
     * @return list of java processes
     * @throws NoSuchMethodException reflection error
     * @throws InvocationTargetException reflection error
     * @throws IllegalAccessException reflection error
     */
    public List listProcesses() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        List ret = new ArrayList();
        Class vmClass = lookupVirtualMachineClass();
        Method method = vmClass.getMethod("list");
        List vmDescriptors = (List) method.invoke(null);
        for (Object descriptor : vmDescriptors) {
            Method idMethod = descriptor.getClass().getMethod("id");
            String id = (String) idMethod.invoke(descriptor);
            Method displayMethod = descriptor.getClass().getMethod("displayName");
            String display = (String) displayMethod.invoke(descriptor);
            ret.add(new ProcessDescription(id, display));
        }
        return ret;
    }

    /**
     * Filter the process list for a regular expression and returns the description. The process this
     * JVM is running in is ignored. If more than one process or no process is found, an exception
     * is raised.
     *
     * @param pPattern regular expression to match
     * @return a process description of the one process found but never null
     * @throws IllegalArgumentException if more than one or no process has been found.
     */
    public ProcessDescription findProcess(Pattern pPattern)
            throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        List ret = new ArrayList();
        String ownId = getOwnProcessId();

        for (ProcessDescription desc : listProcesses()) {
            Matcher matcher = pPattern.matcher(desc.getDisplay());
            if (!desc.getId().equals(ownId) && matcher.find()) {
                ret.add(desc);
            }
        }
        if (ret.size() == 1) {
            return ret.get(0);
        } else if (ret.size() == 0) {
            throw new IllegalArgumentException("No attachable process found matching \"" + pPattern.pattern() + "\"");
        } else {
            StringBuilder buf = new StringBuilder();
            for (ProcessDescription desc : ret) {
                buf.append(desc.getId()).append(" (").append(desc.getDisplay()).append("),");
            }
            throw new IllegalArgumentException("More than one attachable process found matching \"" +
                                               pPattern.pattern() + "\": " + buf.substring(0,buf.length()-1));
        }
    }

    // ========================================================================================================

    /**
     * Get the process id, either directly from option's ID or by looking up a regular expression for java process name
     * (but not this java process)
     *
     * @param pOpts used to get eithe the process Id ({@link OptionsAndArgs#getPid()} or the pattern for matching a
     *        process name ({@link OptionsAndArgs#getProcessPattern()})
     * @return the numeric id as string
     * @throws IllegalArgumentException if a pattern is used and no or more than one process name matches.
     */
    private String getProcessId(OptionsAndArgs pOpts) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        if (pOpts.getPid() != null) {
            String pid = pOpts.getPid();
            if (!isAttachable(pid)) {
                throw new IllegalArgumentException("Cannot attach to process-ID " + pid + ".\nSee --help for possible reasons.");
            }
            return pid;
        } else if (pOpts.getProcessPattern() != null) {
            return findProcess(pOpts.getProcessPattern()).getId();
        } else {
            throw new IllegalArgumentException("No process ID and no process name pattern given");
        }
    }

    // Check whether the given pid is in the list of attachable JVMs
    private boolean isAttachable(String pPid) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        for (ProcessDescription desc : listProcesses()) {
            if (desc.getId().equals(pPid)) {
                return true;
            }
        }
        return false;
    }

    // Try to find out own process id. This is platform dependent and works on Sun/Oracl/OpeneJDKs like the
    // whole agent, so it should be safe
    private String getOwnProcessId() {
        // Format of name is : @
        String name = ManagementFactory.getRuntimeMXBean().getName();
        int endIdx = name.indexOf('@');
        return endIdx != -1 ? name.substring(0,endIdx) : name;
    }

    // lookup virtual machine class
    private Class lookupVirtualMachineClass() {
        try {
            return ToolsClassFinder.lookupClass("com.sun.tools.attach.VirtualMachine");
        } catch (ClassNotFoundException exp) {
            throw new ProcessingException(
                    "Cannot find classes from tools.jar. The heuristics for loading tools.jar which contains\n" +
                    "essential classes for attaching to a running JVM could locate the necessary jar file.\n" +
                    "\n" +
                    "Please call this launcher with a qualified classpath on the command line like\n" +
                    "\n" +
                    "   java -cp path/to/tools.jar:" + options.getJarFileName() + " org.jolokia.jvmagent.client.AgentLauncher [options]  \n",
                    exp,
                    options);
        }
    }


}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy