001/*
002 * SonarQube, open source software quality management tool.
003 * Copyright (C) 2008-2014 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * SonarQube is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * SonarQube is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public License
017 * along with this program; if not, write to the Free Software Foundation,
018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
019 */
020package org.sonar.process;
021
022import org.apache.commons.io.FileUtils;
023
024import java.io.File;
025import java.io.IOException;
026
027/**
028 * Process inter-communication to :
029 * <ul>
030 *   <li>share status of child process</li>
031 *   <li>stop child process</li>
032 * </ul>
033 *
034 * <p/>
035 * It relies on files shared by both processes. Following alternatives were considered but not selected :
036 * <ul>
037 *   <li>JMX beans over RMI: network issues (mostly because of Java reverse-DNS) + requires to configure and open a new port</li>
038 *   <li>simple socket protocol: same drawbacks are RMI connection</li>
039 *   <li>java.lang.Process#destroy(): shutdown hooks are not executed on some OS (mostly MSWindows)</li>
040 *   <li>execute OS-specific commands (for instance kill on *nix): OS-specific, so hell to support. Moreover how to get identify a process ?</li>
041 * </ul>
042 */
043public class ProcessCommands {
044
045  private final File readyFile, stopFile;
046
047  public ProcessCommands(File directory, String processKey) {
048    if (!directory.isDirectory() || !directory.exists()) {
049      throw new IllegalArgumentException("Not a valid directory: " + directory);
050    }
051    this.readyFile = new File(directory, processKey + ".ready");
052    this.stopFile = new File(directory, processKey + ".stop");
053  }
054
055  // visible for tests
056  ProcessCommands(File readyFile, File stopFile) {
057    this.readyFile = readyFile;
058    this.stopFile = stopFile;
059  }
060
061  public void prepare() {
062    deleteFile(readyFile);
063    deleteFile(stopFile);
064  }
065
066  public void endWatch() {
067    // do not fail if files can't be deleted
068    FileUtils.deleteQuietly(readyFile);
069    FileUtils.deleteQuietly(stopFile);
070  }
071
072  public boolean isReady() {
073    return readyFile.exists();
074  }
075
076  /**
077   * To be executed by child process to declare that it's ready
078   */
079  public void setReady() {
080    createFile(readyFile);
081  }
082
083  /**
084   * To be executed by monitor process to ask for child process termination
085   */
086  public void askForStop() {
087    createFile(stopFile);
088  }
089
090  public boolean askedForStop() {
091    return stopFile.exists();
092  }
093
094  File getReadyFile() {
095    return readyFile;
096  }
097
098  File getStopFile() {
099    return stopFile;
100  }
101
102  private void createFile(File file) {
103    try {
104      FileUtils.touch(file);
105    } catch (IOException e) {
106      throw new IllegalStateException(String.format("Fail to create file %s", file), e);
107    }
108  }
109
110  private void deleteFile(File file) {
111    if (file.exists() && !file.delete()) {
112      throw new MessageException(String.format(
113        "Fail to delete file %s. Please check that no SonarQube process is alive", file));
114    }
115  }
116}