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 }