/*
* 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();
}
}