@@ -43,15 +43,15 @@ import org.sonar.runner.impl.Logs; | |||
*/ | |||
public class Main { | |||
private final Exit exit; | |||
private final Shutdown shutdown; | |||
private final Cli cli; | |||
private final Conf conf; | |||
private final RunnerFactory runnerFactory; | |||
private Runner<?> runner; | |||
private BufferedReader inputReader; | |||
Main(Exit exit, Cli cli, Conf conf, RunnerFactory runnerFactory) { | |||
this.exit = exit; | |||
Main(Shutdown shutdown, Cli cli, Conf conf, RunnerFactory runnerFactory) { | |||
this.shutdown = shutdown; | |||
this.cli = cli; | |||
this.conf = conf; | |||
this.runnerFactory = runnerFactory; | |||
@@ -59,9 +59,10 @@ public class Main { | |||
public static void main(String[] args) { | |||
Exit exit = new Exit(); | |||
Shutdown shutdown = new Shutdown(exit); | |||
Cli cli = new Cli(exit).parse(args); | |||
cli.verify(); | |||
Main main = new Main(exit, cli, new Conf(cli), new RunnerFactory()); | |||
Main main = new Main(shutdown, cli, new Conf(cli), new RunnerFactory()); | |||
main.execute(); | |||
} | |||
@@ -84,17 +85,17 @@ public class Main { | |||
} catch (Exception e) { | |||
displayExecutionResult(stats, "FAILURE"); | |||
showError("Error during Sonar runner execution", e, cli.isDisplayStackTrace()); | |||
exit.exit(Exit.ERROR); | |||
shutdown.exit(Exit.ERROR); | |||
} | |||
runner.stop(); | |||
exit.exit(Exit.SUCCESS); | |||
shutdown.exit(Exit.SUCCESS); | |||
} | |||
private void init(Properties p) throws IOException { | |||
SystemInfo.print(); | |||
if (cli.isDisplayVersionOnly()) { | |||
exit.exit(Exit.SUCCESS); | |||
shutdown.exit(Exit.SUCCESS); | |||
} | |||
if (cli.isDisplayStackTrace()) { | |||
@@ -113,9 +114,18 @@ public class Main { | |||
if (inputReader == null) { | |||
inputReader = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8)); | |||
} | |||
shutdown.signalReady(true); | |||
if(shutdown.shouldExit()) { | |||
//exit before displaying message | |||
return false; | |||
} | |||
Logs.info("<Press enter to restart analysis>"); | |||
return inputReader.readLine() != null; | |||
String line = inputReader.readLine(); | |||
shutdown.signalReady(false); | |||
return line != null; | |||
} | |||
private static void displayExecutionResult(Stats stats, String resultMsg) { |
@@ -0,0 +1,92 @@ | |||
/* | |||
* SonarQube Runner - Distribution | |||
* Copyright (C) 2011 SonarSource | |||
* dev@sonar.codehaus.org | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public | |||
* License along with this program; if not, write to the Free Software | |||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 | |||
*/ | |||
package org.sonar.runner; | |||
class Shutdown { | |||
static final int SUCCESS = 0; | |||
static final int ERROR = 1; | |||
private static final long DEFAULT_MAX_WAIT = 10_000; | |||
private long maxWait; | |||
ShutdownHook hook = new ShutdownHook(); | |||
private boolean isReady = false; | |||
private boolean exiting = false; | |||
private Object lock = new Object(); | |||
private Exit exit; | |||
private Thread t; | |||
Shutdown(Exit exit) { | |||
this(exit, DEFAULT_MAX_WAIT); | |||
} | |||
Shutdown(Exit exit, long maxWait) { | |||
this.maxWait = maxWait; | |||
this.exit = exit; | |||
Runtime.getRuntime().addShutdownHook(hook); | |||
} | |||
void exit(int status) { | |||
synchronized (lock) { | |||
signalReady(true); | |||
} | |||
exit.exit(status); | |||
} | |||
void signalReady(boolean ready) { | |||
synchronized (lock) { | |||
System.out.println("READY: " + ready); | |||
this.isReady = ready; | |||
lock.notifyAll(); | |||
} | |||
} | |||
boolean shouldExit() { | |||
synchronized (lock) { | |||
return exiting; | |||
} | |||
} | |||
class ShutdownHook extends Thread { | |||
private ShutdownHook() { | |||
this.setName("shutdown-hook"); | |||
} | |||
@Override | |||
public void run() { | |||
long startTime = System.currentTimeMillis(); | |||
synchronized (lock) { | |||
exiting = true; | |||
while (!isReady) { | |||
long waitTime = startTime + maxWait - System.currentTimeMillis(); | |||
if (waitTime <= 0) { | |||
break; | |||
} | |||
try { | |||
lock.wait(waitTime); | |||
} catch (InterruptedException e) { | |||
// continue | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -30,7 +30,6 @@ import org.sonar.runner.api.Runner; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.UnsupportedEncodingException; | |||
import java.nio.charset.StandardCharsets; | |||
import java.util.Properties; | |||
@@ -44,7 +43,7 @@ import static org.mockito.Mockito.when; | |||
public class MainTest { | |||
@Mock | |||
private Exit exit; | |||
private Shutdown exit; | |||
@Mock | |||
private Cli cli; | |||
@Mock |
@@ -0,0 +1,78 @@ | |||
/* | |||
* SonarQube Runner - Distribution | |||
* Copyright (C) 2011 SonarSource | |||
* dev@sonar.codehaus.org | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public | |||
* License along with this program; if not, write to the Free Software | |||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 | |||
*/ | |||
package org.sonar.runner; | |||
import static org.mockito.Mockito.verify; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
import org.mockito.MockitoAnnotations; | |||
import org.mockito.Mock; | |||
import org.junit.Test; | |||
import org.junit.Before; | |||
public class ShutdownTest { | |||
@Mock | |||
private Exit exit; | |||
private Shutdown shutdown; | |||
@Before | |||
public void setUp() { | |||
MockitoAnnotations.initMocks(this); | |||
shutdown = new Shutdown(exit); | |||
} | |||
@Test | |||
public void testShutdown() { | |||
shutdown.exit(3); | |||
verify(exit).exit(3); | |||
} | |||
@Test(timeout = 60_000) | |||
public void testWaitReady() throws InterruptedException { | |||
shutdown = new Shutdown(exit, 100_000); | |||
shutdown.signalReady(false); | |||
assertThat(shutdown.shouldExit()).isFalse(); | |||
Thread t = new HookCaller(); | |||
t.start(); | |||
Thread.sleep(1000); | |||
assertThat(t.isAlive()).isTrue(); | |||
assertThat(shutdown.shouldExit()).isTrue(); | |||
shutdown.signalReady(true); | |||
t.join(); | |||
} | |||
@Test(timeout = 60_000) | |||
public void testTimeout() throws InterruptedException { | |||
shutdown = new Shutdown(exit, 0); | |||
Thread t = new HookCaller(); | |||
t.start(); | |||
t.join(); | |||
} | |||
private class HookCaller extends Thread { | |||
@Override | |||
public void run() { | |||
shutdown.hook.run(); | |||
} | |||
} | |||
} |