| /* |
| * 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; |
| |
| import java.io.DataInput; |
| import java.io.DataInputStream; |
| import java.io.DataOutput; |
| import java.io.DataOutputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InvalidClassException; |
| import java.net.InetAddress; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.List; |
| |
| import org.apache.geode.DataSerializable; |
| import org.apache.geode.DataSerializer; |
| import org.apache.geode.GemFireIOException; |
| import org.apache.geode.SystemIsRunningException; |
| import org.apache.geode.UncreatedSystemException; |
| import org.apache.geode.UnstartedSystemException; |
| import org.apache.geode.annotations.Immutable; |
| import org.apache.geode.logging.internal.OSProcess; |
| |
| /** |
| * Represents the information about the manager that is stored in its SystemAdmin manager VM's main |
| * thread. |
| * <p> |
| * For internal use only. |
| * |
| * |
| */ |
| public class ManagerInfo implements DataSerializable { |
| private static final long serialVersionUID = 5792809580026325378L; |
| /** |
| * The name of the file the locator will create when the system is started. It will be deleted |
| * when the system is stopped. It contains a single serialize copy of {@link ManagerInfo}. The |
| * file will always be located in the system directory. |
| */ |
| private static final String LOCATOR_INFO_FILE_NAME = ".locator"; |
| /** |
| * The status code constant that means the system is stopped. |
| * <p> |
| * Current value is <code>0</code>. |
| */ |
| public static final int STOPPED_STATUS_CODE = 0; |
| /** |
| * The status code constant that means the system is stopping. |
| * <p> |
| * Current value is <code>1</code>. |
| */ |
| public static final int STOPPING_STATUS_CODE = 1; |
| /** |
| * The status code constant that means the system was killed. |
| * <p> |
| * Current value is <code>2</code>. |
| */ |
| public static final int KILLED_STATUS_CODE = 2; |
| /** |
| * The status code constant that means the system is starting. |
| * <p> |
| * Current value is <code>3</code>. |
| */ |
| public static final int STARTING_STATUS_CODE = 3; |
| /** |
| * The status code constant that means the system is started. |
| * <p> |
| * Current value is <code>4</code>. |
| */ |
| public static final int STARTED_STATUS_CODE = 4; |
| |
| public static void setLocatorStarted(File directory, int port, InetAddress bindAddress) { |
| ManagerInfo.saveManagerInfo(OSProcess.getId(), STARTED_STATUS_CODE, directory, port, |
| bindAddress); |
| } |
| |
| public static File setLocatorStarting(File directory, int port, InetAddress bindAddress) { |
| if (ManagerInfo.isManagerRunning(directory, true)) { |
| throw new SystemIsRunningException(String.format("%s %s is already running.", |
| new Object[] {"Locator", directory.getPath()})); |
| } |
| File result = getManagerInfoFile(directory, true); |
| ManagerInfo.saveManagerInfo(OSProcess.getId(), STARTING_STATUS_CODE, directory, port, |
| bindAddress); |
| return result; |
| } |
| |
| // fix for bug #44059. store the port and address for the locator in the persistent information |
| // so we can use "-dir" to stop the locator. |
| public static void setLocatorStopping(File directory, int port, InetAddress bindAddress) { |
| ManagerInfo.saveManagerInfo(OSProcess.getId(), STOPPING_STATUS_CODE, directory, port, |
| bindAddress); |
| } |
| |
| /** |
| * Saves the manager information to the info file in the given <code>directory</code>. |
| * |
| * @param pid the process id of the manager VM. |
| * @param status the status of the manager |
| * @param directory the manager's directory. |
| * @param port the tcp/ip port for the locator |
| * @param bindAddress the tcp/ip address for the locator |
| * @throws GemFireIOException if the file could not be written. |
| */ |
| private static void saveManagerInfo(int pid, int status, File directory, int port, |
| InetAddress bindAddress) { |
| ManagerInfo info = new ManagerInfo(pid, status, port, bindAddress); |
| File infoFile = getManagerInfoFile(directory, true); |
| try { |
| FileOutputStream ostream = new FileOutputStream(infoFile); |
| DataOutputStream dos = new DataOutputStream(ostream); |
| DataSerializer.writeObject(info, dos); |
| ostream.close(); |
| } catch (IOException io) { |
| throw new GemFireIOException( |
| String.format("Could not write file %s.", infoFile), io); |
| } |
| } |
| |
| /** |
| * Gets the process ID of the manager. |
| */ |
| public int getManagerProcessId() { |
| return this.managerPid; |
| } |
| |
| /** |
| * Gets the status of the manager. |
| */ |
| public int getManagerStatus() { |
| return this.managerStatus; |
| } |
| |
| /** |
| * get the port number of the manager |
| */ |
| public int getManagerPort() { |
| return this.port; |
| } |
| |
| /** |
| * get the bind address of the manager |
| */ |
| public InetAddress getManagerAddress() { |
| return this.bindAddress; |
| } |
| |
| @Immutable |
| static final List<String> statusNames = |
| Collections.unmodifiableList(Arrays.asList("stopped", |
| "stopping", |
| "killed", |
| "starting", |
| "running")); |
| |
| /** |
| * Gets the string representation for the given <code>status</code> int code. |
| */ |
| public static String statusToString(int status) { |
| return statusNames.get(status); |
| } |
| |
| /** |
| * Gets the status code for the given <code>statusName</code>. |
| * |
| * @throws IllegalArgumentException if an unknown status name is given. |
| */ |
| public static int statusNameToCode(String statusName) { |
| for (int i = STOPPED_STATUS_CODE; i <= STARTED_STATUS_CODE; i++) { |
| if (statusNames.get(i).equalsIgnoreCase(statusName)) { |
| return i; |
| } |
| } |
| throw new IllegalArgumentException( |
| String.format("Unknown statusName %s", statusName)); |
| } |
| |
| public static ManagerInfo loadLocatorInfo(File directory) { |
| return loadManagerInfo(directory, true); |
| } |
| |
| private static ManagerInfo loadManagerInfo(File directory, boolean locator) { |
| if (!directory.exists() || !directory.isDirectory()) { |
| throw new UncreatedSystemException( |
| String.format("%s does not exist or is not a directory.", |
| directory.getPath())); |
| } |
| File infoFile = getManagerInfoFile(directory, locator); |
| if (!infoFile.exists()) { |
| throw new UnstartedSystemException(String.format("The info file %s does not exist.", |
| infoFile.getPath())); |
| } |
| try { |
| FileInputStream fis = new FileInputStream(infoFile); |
| if (fis.available() == 0) { |
| throw new GemFireIOException( |
| String.format( |
| "Could not load file %s because the file is empty. Wait for the %s to finish starting.", |
| new Object[] {infoFile, (locator ? "locator" : "system")}), |
| null); |
| } |
| DataInputStream dis = new DataInputStream(fis); |
| ManagerInfo result = (ManagerInfo) DataSerializer.readObject(dis); |
| fis.close(); |
| return result; |
| } catch (IOException io) { |
| throw new GemFireIOException( |
| String.format("Could not load file %s.", infoFile), io); |
| } catch (ClassNotFoundException ex) { |
| throw new GemFireIOException( |
| String.format("Could not load file %s because a class could not be found.", |
| infoFile), |
| ex); |
| } |
| } |
| |
| public static File getLocatorInfoFile(File directory) { |
| return getManagerInfoFile(directory, true); |
| } |
| |
| private static File getManagerInfoFile(File directory, boolean locator) { |
| if (!locator) { |
| throw new IllegalArgumentException( |
| "Only locators are supported"); |
| } |
| File res = new File(directory, LOCATOR_INFO_FILE_NAME); |
| try { |
| res = res.getCanonicalFile(); |
| } catch (IOException ex) { |
| res = res.getAbsoluteFile(); |
| } |
| return res; |
| } |
| |
| public static String getLocatorStatusCodeString(File directory) { |
| return statusToString(getLocatorStatusCode(directory)); |
| } |
| |
| public static int getLocatorStatusCode(File directory) { |
| return getManagerStatusCode(directory, true); |
| } |
| |
| private static int getManagerStatusCode(File directory, boolean locator) { |
| boolean interrupted = false; |
| try { |
| ManagerInfo mi = ManagerInfo.loadManagerInfo(directory, locator); |
| return mi.getManagerStatus(); |
| |
| } catch (UnstartedSystemException ex) { |
| return STOPPED_STATUS_CODE; |
| } catch (GemFireIOException ex) { |
| // wait a bit and try again in case we caught the manager rewriting |
| // its info file |
| try { |
| Thread.sleep(1000); |
| } catch (InterruptedException ignore) { |
| interrupted = true; |
| } |
| try { |
| ManagerInfo.loadManagerInfo(directory, locator); |
| // all is well so do a recursive call |
| return getManagerStatusCode(directory, locator); |
| } catch (UnstartedSystemException ignore) { |
| return STOPPED_STATUS_CODE; |
| } |
| } finally { |
| if (interrupted) { |
| Thread.currentThread().interrupt(); |
| } |
| } |
| } |
| |
| public static boolean isLocatorStarted(File directory) { |
| return isManagerStarted(directory, true); |
| } |
| |
| private static boolean isManagerStarted(File directory, boolean locator) { |
| try { |
| ManagerInfo mi = loadManagerInfo(directory, locator); |
| int status = mi.getManagerStatus(); |
| if (status != STARTED_STATUS_CODE) { |
| return false; |
| } |
| return true; |
| } catch (UnstartedSystemException ignore) { |
| return false; |
| } catch (GemFireIOException ex) { |
| Throwable cause = ex.getCause(); |
| if (cause == null) { |
| // this happens when the file was zero size |
| return false; |
| } else if (cause instanceof InvalidClassException) { |
| // This happens when we have a serialVersionUID mismatch. |
| // We don't want to hide this so throw the exception |
| throw ex; |
| } else { |
| return false; |
| } |
| } |
| } |
| |
| public static boolean isLocatorRunning(File directory) { |
| return isManagerRunning(directory, true); |
| } |
| |
| private static boolean isManagerRunning(File directory, boolean locator) { |
| try { |
| ManagerInfo mi = loadManagerInfo(directory, locator); |
| int status = mi.getManagerStatus(); |
| if (status != STARTED_STATUS_CODE && status != STARTING_STATUS_CODE |
| && status != STOPPING_STATUS_CODE) { |
| return false; |
| } |
| return true; |
| } catch (UnstartedSystemException ignore) { |
| return false; |
| } catch (GemFireIOException ex) { |
| Throwable cause = ex.getCause(); |
| if (cause == null) { |
| // this happens when the file was zero size |
| // This indicates the manager is changing its info file |
| return true; |
| } else if (cause instanceof InvalidClassException) { |
| // This happens when we have a serialVersionUID mismatch. |
| // We don't want to hide this so throw the exception |
| throw ex; |
| } else { |
| // This indicates the manager is changing its info file |
| return true; |
| } |
| } |
| } |
| |
| public static boolean isLocatorStopped(File directory) { |
| return isManagerStopped(directory, true); |
| } |
| |
| private static boolean isManagerStopped(File directory, boolean locator) { |
| try { |
| ManagerInfo mi = loadManagerInfo(directory, locator); |
| if (!OSProcess.exists(mi.getManagerProcessId())) { |
| return true; |
| } |
| return false; |
| } catch (UnstartedSystemException ignore) { |
| return true; |
| } catch (GemFireIOException ex) { |
| Throwable cause = ex.getCause(); |
| if (cause == null) { |
| // this happens when the file was zero size |
| return false; |
| } else if (cause instanceof InvalidClassException) { |
| // This happens when we have a serialVersionUID mismatch. |
| // We don't want to hide this so throw the exception |
| throw ex; |
| } else { |
| return false; |
| } |
| } |
| } |
| |
| /** |
| * Constructs a manager info instance given the process id of the manager VM. |
| * |
| * @param port TODO |
| * @param bindAddress TODO |
| */ |
| private ManagerInfo(int pid, int status, int port, InetAddress bindAddress) { |
| this.managerPid = pid; |
| this.managerStatus = status; |
| this.port = port; |
| this.bindAddress = bindAddress; |
| } |
| |
| /** |
| * Constructor for <code>DataSerializable</code> |
| */ |
| public ManagerInfo() {} |
| |
| // instance variables |
| private int managerPid; |
| private int managerStatus; |
| private int port; |
| private InetAddress bindAddress; |
| |
| @Override |
| public void toData(DataOutput out) throws IOException { |
| out.writeInt(this.managerPid); |
| out.writeInt(this.managerStatus); |
| out.writeInt(this.port); |
| if (this.bindAddress == null) { |
| out.writeByte(0); |
| } else { |
| byte[] address = this.bindAddress.getAddress(); |
| out.writeByte(address.length); |
| out.write(address, 0, address.length); |
| } |
| } |
| |
| @Override |
| public void fromData(DataInput in) throws IOException, ClassNotFoundException { |
| |
| this.managerPid = in.readInt(); |
| this.managerStatus = in.readInt(); |
| this.port = in.readInt(); |
| byte len = in.readByte(); |
| if (len > 0) { |
| byte[] addr = new byte[len]; |
| in.readFully(addr); |
| this.bindAddress = InetAddress.getByAddress(addr); |
| } |
| } |
| } |