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.slf4j.LoggerFactory; 023 024 public class ProcessEntryPoint implements Stoppable { 025 026 public static final String PROPERTY_PROCESS_KEY = "process.key"; 027 public static final String PROPERTY_TERMINATION_TIMEOUT = "process.terminationTimeout"; 028 public static final String PROPERTY_SHARED_PATH = "process.sharedDir"; 029 030 private final Props props; 031 private final Lifecycle lifecycle = new Lifecycle(); 032 private final ProcessCommands commands; 033 private final SystemExit exit; 034 private volatile Monitored monitored; 035 private volatile StopperThread stopperThread; 036 private final StopWatcher stopWatcher; 037 038 // new Runnable() is important to avoid conflict of call to ProcessEntryPoint#stop() with Thread#stop() 039 private Thread shutdownHook = new Thread(new Runnable() { 040 @Override 041 public void run() { 042 exit.setInShutdownHook(); 043 stop(); 044 } 045 }); 046 047 ProcessEntryPoint(Props props, SystemExit exit, ProcessCommands commands) { 048 this.props = props; 049 this.exit = exit; 050 this.commands = commands; 051 this.stopWatcher = new StopWatcher(commands, this); 052 } 053 054 public Props getProps() { 055 return props; 056 } 057 058 public String getKey() { 059 return props.nonNullValue(PROPERTY_PROCESS_KEY); 060 } 061 062 /** 063 * Launch process and waits until it's down 064 */ 065 public void launch(Monitored mp) { 066 if (!lifecycle.tryToMoveTo(Lifecycle.State.STARTING)) { 067 throw new IllegalStateException("Already started"); 068 } 069 commands.prepare(); 070 monitored = mp; 071 072 try { 073 LoggerFactory.getLogger(getClass()).warn("Starting " + getKey()); 074 Runtime.getRuntime().addShutdownHook(shutdownHook); 075 stopWatcher.start(); 076 077 monitored.start(); 078 boolean ready = false; 079 while (!ready) { 080 ready = monitored.isReady(); 081 Thread.sleep(200L); 082 } 083 084 // notify monitor that process is ready 085 commands.setReady(); 086 087 if (lifecycle.tryToMoveTo(Lifecycle.State.STARTED)) { 088 monitored.awaitStop(); 089 } 090 } catch (Exception e) { 091 LoggerFactory.getLogger(getClass()).warn("Fail to start " + getKey(), e); 092 093 } finally { 094 stop(); 095 } 096 } 097 098 boolean isStarted() { 099 return lifecycle.getState() == Lifecycle.State.STARTED; 100 } 101 102 /** 103 * Blocks until stopped in a timely fashion (see {@link org.sonar.process.StopperThread}) 104 */ 105 void stop() { 106 stopAsync(); 107 try { 108 // stopperThread is not null for sure 109 // join() does nothing if thread already finished 110 stopperThread.join(); 111 lifecycle.tryToMoveTo(Lifecycle.State.STOPPED); 112 } catch (InterruptedException e) { 113 // nothing to do, the process is going to be exited 114 } 115 exit.exit(0); 116 } 117 118 @Override 119 public void stopAsync() { 120 if (lifecycle.tryToMoveTo(Lifecycle.State.STOPPING)) { 121 stopperThread = new StopperThread(monitored, commands, Long.parseLong(props.nonNullValue(PROPERTY_TERMINATION_TIMEOUT))); 122 stopperThread.start(); 123 stopWatcher.stopWatching(); 124 } 125 } 126 127 Lifecycle.State getState() { 128 return lifecycle.getState(); 129 } 130 131 Thread getShutdownHook() { 132 return shutdownHook; 133 } 134 135 public static ProcessEntryPoint createForArguments(String[] args) { 136 Props props = ConfigurationUtils.loadPropsFromCommandLineArgs(args); 137 ProcessCommands commands = new ProcessCommands( 138 props.nonNullValueAsFile(PROPERTY_SHARED_PATH), props.nonNullValue(PROPERTY_PROCESS_KEY)); 139 return new ProcessEntryPoint(props, new SystemExit(), commands); 140 } 141 }