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     */
020    package org.sonar.process;
021    
022    import org.apache.commons.io.FileUtils;
023    
024    import java.io.File;
025    import 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     */
043    public 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    }