/* * Licensed to the Apache Software Foundation (ASF) under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional information regarding * copyright ownership. The ASF licenses this file to You 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. */ package org.apache.geode.internal.process; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.lang.management.ManagementFactory; import org.apache.geode.internal.util.IOUtils; /** * Utility operations for processes such as identifying the process id (pid). * * @since GemFire 7.0 */ public final class ProcessUtils { private static InternalProcessUtils internal = initializeInternalProcessUtils(); private ProcessUtils() {} /** * Returns the pid for this process. * * @throws PidUnavailableException if parsing the pid from the name of the RuntimeMXBean fails * * @see java.lang.management.RuntimeMXBean#getName() */ public static int identifyPid() throws PidUnavailableException { return identifyPid(ManagementFactory.getRuntimeMXBean().getName()); } /** * Returns the pid for this process using the specified name from RuntimeMXBean. * * @throws PidUnavailableException if parsing the pid from the RuntimeMXBean name fails */ public static int identifyPid(final String name) throws PidUnavailableException { try { final int index = name.indexOf("@"); if (index < 0) { throw new PidUnavailableException("Unable to parse pid from " + name); } return Integer.valueOf(name.substring(0, index)); } catch (NumberFormatException e) { throw new PidUnavailableException("Unable to parse pid from " + name, e); } } /** * Returns true if a process identified by the process id is currently running on this host * machine. * * @param pid process id to check for * @return true if the pid matches a currently running process */ public static boolean isProcessAlive(final int pid) { return internal.isProcessAlive(pid); } /** * Returns true if a process identified by the specified Process is currently running on this host * machine. * * @param process the Process to check * @return true if the Process is a currently running process */ public static boolean isProcessAlive(final Process process) { return process.isAlive(); } /** * Returns true if a process identified by the process id was running on this host machine and has * been terminated by this operation. * * @param pid process id * @return true if the process was terminated by this operation */ public static boolean killProcess(final int pid) { return internal.killProcess(pid); } public static int readPid(final File pidFile) throws IOException { BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(pidFile)); return Integer.parseInt(reader.readLine()); } finally { IOUtils.close(reader); } } /** * Returns true if Attach API or JNA NativeCalls is available for killing process or checking if * it is alive. */ public static boolean isAvailable() { return internal.isAvailable(); } /** * Returns true if Attach API is available for checking status. */ public static boolean isAttachApiAvailable() { return internal.isAttachApiAvailable(); } private static InternalProcessUtils initializeInternalProcessUtils() { // 1) prefer Attach because it filters out non-JVM processes try { Class.forName("com.sun.tools.attach.VirtualMachine"); Class.forName("com.sun.tools.attach.VirtualMachineDescriptor"); return new AttachProcessUtils(); } catch (ClassNotFoundException e) { // fall through } catch (LinkageError e) { // fall through } // 2) try NativeCalls but make sure it doesn't throw UnsupportedOperationException try { // TODO: get rid of Class.forName usage if NativeCalls always safely loads Class.forName("org.apache.geode.internal.shared.NativeCalls"); NativeProcessUtils inst = new NativeProcessUtils(); boolean result = inst.isProcessAlive(identifyPid()); if (result) { return inst; } } catch (ClassNotFoundException e) { // fall through } catch (LinkageError e) { // fall through } catch (PidUnavailableException e) { // fall through TODO:KIRK log warning?? } catch (UnsupportedOperationException e) { // fall through } // 3) TODO: log warning and then proceed with no-op return new InternalProcessUtils() { @Override public boolean isProcessAlive(int pid) { return false; } @Override public boolean killProcess(int pid) { return false; } @Override public boolean isAvailable() { return false; } @Override public boolean isAttachApiAvailable() { return false; } }; } /** * Defines the SPI for ProcessUtils */ interface InternalProcessUtils { public boolean isProcessAlive(int pid); public boolean killProcess(int pid); public boolean isAvailable(); public boolean isAttachApiAvailable(); } }