// only static stuff
}
+ public static final String WEB_SERVER_NAME = "web";
+ public static final String SEARCH_SERVER_NAME = "search";
+
public static ObjectName objectName(String name) {
try {
return new ObjectName("org.sonar", "name", name);
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
private final static Logger LOGGER = LoggerFactory.getLogger(Monitor.class);
private volatile List<ProcessWrapper> processes;
- private volatile Map<String, Long> pings;
+ private final Map<String, Long> pings;
private final ScheduledFuture<?> watch;
private final ScheduledExecutorService monitor;
public Monitor() {
super("Process Monitor");
processes = new ArrayList<ProcessWrapper>();
- pings = new HashMap<String, Long>();
+ pings = new ConcurrentHashMap<String, Long>();
monitor = Executors.newScheduledThreadPool(1);
watch = monitor.scheduleWithFixedDelay(new ProcessWatch(), 0L, PING_DELAY_MS, TimeUnit.MILLISECONDS);
}
@Override
public void run() {
try {
- while (true) {
+ boolean ok = true;
+ while (ok) {
for (ProcessWrapper process : processes) {
if (!processIsValid(process)) {
- LOGGER.warn("Monitor::run() -- Process '{}' is not valid. Exiting monitor", process.getName());
+ ok = false;
interrupt();
}
}
- Thread.sleep(PING_DELAY_MS);
+ if (ok) {
+ Thread.sleep(PING_DELAY_MS);
+ }
}
} catch (InterruptedException e) {
LOGGER.debug("Monitoring thread is interrupted");
@Override
public void terminate() {
- processes.clear();
if (!monitor.isShutdown()) {
monitor.shutdownNow();
}
if (!watch.isCancelled()) {
watch.cancel(true);
}
+
+ for (int i=processes.size()-1 ; i>=0 ; i--) {
+ processes.get(i).terminate();
+ }
+ processes.clear();
+ interrupt();
}
}
waitUntilFinish(errorGobbler);
ProcessUtils.closeStreams(process);
FileUtils.deleteQuietly(propertiesFile);
- processMXBean = null;
}
- LOGGER.trace("ProcessWrapper::run() END");
}
public boolean isReady() {
@Override
public void terminate() {
- if (processMXBean != null) {
+ if (processMXBean != null && process != null) {
// Send the terminate command to process in order to gracefully shutdown.
// Then hardly kill it if it didn't terminate in 30 seconds
ScheduledExecutorService killer = Executors.newScheduledThreadPool(1);
} catch (Exception ignored) {
// ignore
+ ignored.printStackTrace();
} finally {
killer.shutdownNow();
}
// process is not monitored through JMX, but killing it though
ProcessUtils.destroyQuietly(process);
}
+ processMXBean = null;
}
public boolean waitForReady() throws InterruptedException {
long wait = 500L;
while (now < READY_TIMEOUT_MS) {
try {
+ if (processMXBean == null) {
+ return false;
+ }
if (processMXBean.isReady()) {
return true;
}
package org.sonar.server.app;
import org.apache.catalina.connector.Connector;
+import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.Tomcat;
import org.apache.commons.io.FileUtils;
import org.slf4j.LoggerFactory;
private final Props props;
private Tomcat tomcat = null;
private Thread hook = null;
- private boolean stopping = false, ready = false;
+ private boolean ready = false;
EmbeddedTomcat(Props props) {
this.props = props;
Logging.configure(tomcat, props);
Connectors.configure(tomcat, props);
- Webapp.configure(tomcat, props);
+ StandardContext webappContext = Webapp.configure(tomcat, props);
ProcessUtils.addSelfShutdownHook(this);
tomcat.start();
- ready = true;
- tomcat.getServer().await();
+
+ if (webappContext.getState().isAvailable()) {
+ ready = true;
+ tomcat.getServer().await();
+ }
} catch (Exception e) {
throw new IllegalStateException("Fail to start web server", e);
} finally {
@Override
public void terminate() {
- if (tomcat != null && !stopping) {
- try {
- stopping = true;
- tomcat.stop();
- tomcat.destroy();
- } catch (Exception e) {
- LoggerFactory.getLogger(EmbeddedTomcat.class).error("Fail to stop web service", e);
+ if (tomcat != null) {
+ synchronized (tomcat) {
+ if (tomcat.getServer().getState().isAvailable()) {
+ try {
+ tomcat.stop();
+ tomcat.destroy();
+ } catch (Exception e) {
+ LoggerFactory.getLogger(EmbeddedTomcat.class).error("Fail to stop web service", e);
+ }
+ }
}
}
- tomcat = null;
- stopping = false;
ready = false;
FileUtils.deleteQuietly(tomcatBasedir());
}
private static final String RAILS_ENV = "rails.env";
private static final String PROPERTY_CONTEXT = "sonar.web.context";
- static void configure(Tomcat tomcat, Props props) {
+ static StandardContext configure(Tomcat tomcat, Props props) {
try {
String webDir = props.of("sonar.path.web");
LoggerFactory.getLogger(Webapp.class).info("Webapp directory: " + webDir);
}
configureRailsMode(props, context);
context.setJarScanner(new NullJarScanner());
+ return context;
} catch (Exception e) {
throw new IllegalStateException("Fail to configure webapp", e);
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
+
import java.util.Enumeration;
import java.util.Properties;
// unexpected errors
LoggerFactory.getLogger(getClass()).error("Fail to start server", t);
stopQuietly();
- throw new IllegalStateException("Fail to start server", t);
+ throw new IllegalStateException("Fail to start webapp", t);
}
}
package org.sonar.server.plugins;
import com.google.common.base.Joiner;
-import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.io.FileUtils;
private final ServerUpgradeStatus serverUpgradeStatus;
public ServerPluginJarsInstaller(Server server, ServerUpgradeStatus serverUpgradeStatus,
- DefaultServerFileSystem fs, ServerPluginJarInstaller installer) {
+ DefaultServerFileSystem fs, ServerPluginJarInstaller installer) {
this.server = server;
this.serverUpgradeStatus = serverUpgradeStatus;
this.fs = fs;
private void moveDownloadedPlugins() {
if (fs.getDownloadedPluginsDir().exists()) {
- Collection<File> sourceFiles = FileUtils.listFiles(fs.getDownloadedPluginsDir(), new String[]{"jar"}, false);
+ Collection<File> sourceFiles = FileUtils.listFiles(fs.getDownloadedPluginsDir(), new String[] {"jar"}, false);
for (File sourceFile : sourceFiles) {
overridePlugin(sourceFile, true);
}
}
}
-
private void overridePlugin(File sourceFile, boolean deleteSource) {
File destDir = fs.getUserPluginsDir();
File destFile = new File(destDir, sourceFile.getName());
public List<String> getUninstalls() {
List<String> names = Lists.newArrayList();
if (fs.getTrashPluginsDir().exists()) {
- List<File> files = (List<File>) FileUtils.listFiles(fs.getTrashPluginsDir(), new String[]{"jar"}, false);
+ List<File> files = (List<File>) FileUtils.listFiles(fs.getTrashPluginsDir(), new String[] {"jar"}, false);
for (File file : files) {
names.add(file.getName());
}
public void cancelUninstalls() {
if (fs.getTrashPluginsDir().exists()) {
- List<File> files = (List<File>) FileUtils.listFiles(fs.getTrashPluginsDir(), new String[]{"jar"}, false);
+ List<File> files = (List<File>) FileUtils.listFiles(fs.getTrashPluginsDir(), new String[] {"jar"}, false);
for (File file : files) {
try {
FileUtils.moveFileToDirectory(file, fs.getUserPluginsDir(), false);
private void deploy(DefaultPluginMetadata plugin) {
LOG.info("Deploy plugin {}", Joiner.on(" / ").skipNulls().join(plugin.getName(), plugin.getVersion(), plugin.getImplementationBuild()));
- Preconditions.checkState(plugin.isCompatibleWith(server.getVersion()),
- "Plugin %s needs a more recent version of SonarQube than %s. At least %s is expected",
- plugin.getKey(), server.getVersion(), plugin.getSonarVersion());
+ if (!plugin.isCompatibleWith(server.getVersion())) {
+ throw MessageException.of(String.format("Plugin %s needs a more recent version of SonarQube than %s. At least %s is expected",
+ plugin.getKey(), server.getVersion(), plugin.getSonarVersion()));
+ }
try {
File pluginDeployDir = new File(fs.getDeployedPluginsDir(), plugin.getKey());
public class App implements ProcessMXBean {
- static final String SONAR_WEB_PROCESS = "web";
- static final String SONAR_SEARCH_PROCESS = "search";
-
private final Installation installation;
private final Monitor monitor = new Monitor();
Logger logger = LoggerFactory.getLogger(getClass());
monitor.start();
- elasticsearch = new ProcessWrapper(SONAR_SEARCH_PROCESS)
+ elasticsearch = new ProcessWrapper(JmxUtils.SEARCH_SERVER_NAME)
.setWorkDir(installation.homeDir())
.setJmxPort(Integer.parseInt(installation.prop(DefaultSettings.ES_JMX_PORT_KEY)))
.addJavaOpts(installation.prop(DefaultSettings.ES_JAVA_OPTS_KEY))
if (elasticsearch.waitForReady()) {
logger.info("Search server is ready");
- server = new ProcessWrapper(SONAR_WEB_PROCESS)
+ server = new ProcessWrapper(JmxUtils.WEB_SERVER_NAME)
.setWorkDir(installation.homeDir())
.setJmxPort(Integer.parseInt(installation.prop(DefaultSettings.WEB_JMX_PORT_KEY)))
.addJavaOpts(installation.prop(DefaultSettings.WEB_JAVA_OPTS_KEY))
@Override
public void terminate() {
- monitor.terminate();
- monitor.interrupt();
+ LoggerFactory.getLogger(App.class).info("Stopping");
+ if (monitor.isAlive()) {
+ monitor.terminate();
+ monitor.interrupt();
+ }
if (server != null) {
server.terminate();
}