org.apache.geode.internal.process.ControllableProcess.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.geode.internal.process.ControllableProcess.java

Source

/*
 * 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 static org.apache.commons.lang.Validate.notNull;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.UncheckedIOException;

import org.apache.logging.log4j.Logger;

import org.apache.geode.distributed.AbstractLauncher.ServiceState;
import org.apache.geode.internal.logging.LogService;
import org.apache.geode.internal.process.ControlFileWatchdog.ControlRequestHandler;

/**
 * Creates the {@link PidFile} and uses {@link ControlFileWatchdog} to monitor the directory for
 * creation of stop or status request files.
 * 
 * @since GemFire 8.0
 */
public class ControllableProcess {
    private static final Logger logger = LogService.getLogger();

    private final File directory;
    private final LocalProcessLauncher launcher;
    private final ControlFileWatchdog stopRequestFileWatchdog;
    private final ControlFileWatchdog statusRequestFileWatchdog;

    public ControllableProcess(final ControlNotificationHandler handler, final File directory,
            final ProcessType processType, final boolean force)
            throws FileAlreadyExistsException, IOException, PidUnavailableException {
        this(directory, processType, force, createPidFile(directory, processType), createStopHandler(handler),
                createStatusHandler(handler, directory, processType));
    }

    private ControllableProcess(final File directory, final ProcessType processType, final boolean force,
            final File pidFile, final ControlRequestHandler stopHandler, final ControlRequestHandler statusHandler)
            throws FileAlreadyExistsException, IOException, PidUnavailableException {
        this(directory, processType, createLocalProcessLauncher(pidFile, force),
                createStopRequestFileWatchdog(directory, processType, stopHandler),
                createStatusRequestFileWatchdog(directory, processType, statusHandler));
    }

    ControllableProcess(final File directory, final ProcessType processType, final LocalProcessLauncher launcher,
            final ControlFileWatchdog stopRequestFileWatchdog,
            final ControlFileWatchdog statusRequestFileWatchdog) {
        notNull(directory, "Invalid directory '" + directory + "' specified");
        notNull(processType, "Invalid processType '" + processType + "' specified");
        notNull(launcher, "Invalid launcher '" + launcher + "' specified");
        notNull(stopRequestFileWatchdog,
                "Invalid stopRequestFileWatchdog '" + stopRequestFileWatchdog + "' specified");
        notNull(statusRequestFileWatchdog,
                "Invalid statusRequestFileWatchdog '" + statusRequestFileWatchdog + "' specified");

        this.directory = directory;
        this.launcher = launcher;
        this.stopRequestFileWatchdog = stopRequestFileWatchdog;
        this.statusRequestFileWatchdog = statusRequestFileWatchdog;

        deleteFiles(directory, processType);
        stopRequestFileWatchdog.start();
        statusRequestFileWatchdog.start();
    }

    /**
     * Returns the process id (PID).
     * 
     * @return the process id (PID)
     */
    public int getPid() {
        return launcher.getPid();
    }

    /**
     * Returns the PID file.
     * 
     * @return the PID file
     */
    public File getPidFile() {
        return launcher.getPidFile();
    }

    public File getDirectory() {
        return directory;
    }

    public void stop() {
        boolean interrupted = false;
        try {
            interrupted = stop(statusRequestFileWatchdog);
            interrupted = stop(stopRequestFileWatchdog) || interrupted;
            launcher.close();
        } finally {
            if (interrupted) {
                Thread.currentThread().interrupt();
            }
        }
    }

    public void stop(final boolean deletePidFileOnStop) {
        boolean interrupted = false;
        try {
            interrupted = stop(statusRequestFileWatchdog);
            interrupted = stop(stopRequestFileWatchdog) || interrupted;
            launcher.close(deletePidFileOnStop);
        } finally {
            if (interrupted) {
                Thread.currentThread().interrupt();
            }
        }
    }

    private boolean stop(final ControlFileWatchdog fileWatchdog) {
        boolean interrupted = false;
        try {
            fileWatchdog.stop();
        } catch (InterruptedException e) {
            interrupted = true;
            logger.warn("Interrupted while stopping status handler for controllable process.", e);
        }
        return interrupted;
    }

    private void deleteFiles(final File directory, final ProcessType processType) {
        try {
            deleteFileWithValidation(new File(directory, processType.getStatusRequestFileName()),
                    "statusRequestFile");
            deleteFileWithValidation(new File(directory, processType.getStatusFileName()), "statusFile");
            deleteFileWithValidation(new File(directory, processType.getStopRequestFileName()), "stopRequestFile");
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    private static File createPidFile(final File directory, final ProcessType processType) {
        return new File(directory, processType.getPidFileName());
    }

    private static LocalProcessLauncher createLocalProcessLauncher(final File pidFile, final boolean force)
            throws FileAlreadyExistsException, IOException, PidUnavailableException {
        return new LocalProcessLauncher(pidFile, force);
    }

    private static ControlRequestHandler createStopHandler(final ControlNotificationHandler handler) {
        return handler::handleStop;
    }

    private static ControlRequestHandler createStatusHandler(final ControlNotificationHandler handler,
            final File directory, final ProcessType processType) {
        return () -> {
            writeStatusToFile(fetchStatusWithValidation(handler), directory, processType);
        };
    }

    private static ControlFileWatchdog createStopRequestFileWatchdog(final File directory,
            final ProcessType processType, final ControlRequestHandler stopHandler) {
        return new ControlFileWatchdog(directory, processType.getStopRequestFileName(), stopHandler, false);
    }

    private static ControlFileWatchdog createStatusRequestFileWatchdog(final File directory,
            final ProcessType processType, final ControlRequestHandler statusHandler) {
        return new ControlFileWatchdog(directory, processType.getStatusRequestFileName(), statusHandler, false);
    }

    private static String fetchStatusWithValidation(final ControlNotificationHandler handler) {
        ServiceState<?> state = handler.handleStatus();
        if (state == null) {
            throw new IllegalStateException("Null ServiceState is invalid");
        }

        String jsonContent = state.toJson();
        if (jsonContent == null) {
            throw new IllegalStateException("Null JSON for status is invalid");
        } else if (jsonContent.isEmpty()) {
            throw new IllegalStateException("Empty JSON for status is invalid");
        }

        return jsonContent;
    }

    private static void deleteFileWithValidation(final File file, final String fileNameForMessage)
            throws IOException {
        if (file.exists()) {
            if (!file.delete()) {
                throw new IOException(
                        "Unable to delete " + fileNameForMessage + "'" + file.getCanonicalPath() + "'");
            }
        }
    }

    private static void writeStatusToFile(final String jsonContent, final File directory,
            final ProcessType processType) throws IOException {
        File statusFile = new File(directory, processType.getStatusFileName());
        File statusFileTmp = new File(directory, processType.getStatusFileName() + ".tmp");

        deleteFileWithValidation(statusFile, "statusFile");
        deleteFileWithValidation(statusFileTmp, "statusFileTmp");

        if (!statusFileTmp.createNewFile()) {
            throw new IOException("Unable to create statusFileTmp '" + statusFileTmp.getCanonicalPath() + "'");
        }

        FileWriter writer = new FileWriter(statusFileTmp);
        writer.write(jsonContent);
        writer.flush();
        writer.close();

        if (!statusFileTmp.renameTo(statusFile)) {
            throw new IOException("Unable to rename statusFileTmp '" + statusFileTmp.getCanonicalPath() + "' to '"
                    + statusFile.getCanonicalPath() + "'");
        }
    }
}