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 }