aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-process
diff options
context:
space:
mode:
Diffstat (limited to 'server/sonar-process')
-rw-r--r--server/sonar-process/src/main/java/org/sonar/process/AllProcessesCommands.java111
-rw-r--r--server/sonar-process/src/main/java/org/sonar/process/DefaultProcessCommands.java8
-rw-r--r--server/sonar-process/src/main/java/org/sonar/process/Monitored.java7
-rw-r--r--server/sonar-process/src/main/java/org/sonar/process/ProcessCommands.java22
-rw-r--r--server/sonar-process/src/main/java/org/sonar/process/ProcessEntryPoint.java8
-rw-r--r--server/sonar-process/src/test/java/org/sonar/process/AllProcessesCommandsTest.java32
-rw-r--r--server/sonar-process/src/test/java/org/sonar/process/DefaultProcessCommandsTest.java6
-rw-r--r--server/sonar-process/src/test/java/org/sonar/process/ProcessEntryPointTest.java4
-rw-r--r--server/sonar-process/src/test/java/org/sonar/process/test/HttpProcess.java2
-rw-r--r--server/sonar-process/src/test/java/org/sonar/process/test/InfiniteTerminationProcess.java2
-rw-r--r--server/sonar-process/src/test/java/org/sonar/process/test/StandardProcess.java2
11 files changed, 111 insertions, 93 deletions
diff --git a/server/sonar-process/src/main/java/org/sonar/process/AllProcessesCommands.java b/server/sonar-process/src/main/java/org/sonar/process/AllProcessesCommands.java
index e174984a88b..52f5bcfd9fd 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/AllProcessesCommands.java
+++ b/server/sonar-process/src/main/java/org/sonar/process/AllProcessesCommands.java
@@ -32,46 +32,50 @@ import static org.sonar.process.ProcessCommands.MAX_PROCESSES;
* Process inter-communication to :
* <ul>
* <li>share status of child process</li>
- * <li>stop child process</li>
+ * <li>stop/restart child process</li>
* </ul>
*
- * <p/>
- * It relies on files shared by both processes. Following alternatives were considered but not selected :
+ * <p>
+ * It relies on a single file accessed by all processes through a {@link MappedByteBuffer}.<br/>
+ * Following alternatives were considered but not selected :
* <ul>
* <li>JMX beans over RMI: network issues (mostly because of Java reverse-DNS) + requires to configure and open a new port</li>
* <li>simple socket protocol: same drawbacks are RMI connection</li>
* <li>java.lang.Process#destroy(): shutdown hooks are not executed on some OS (mostly MSWindows)</li>
* <li>execute OS-specific commands (for instance kill on *nix): OS-specific, so hell to support. Moreover how to get identify a process ?</li>
* </ul>
+ * </p>
+ *
+ * <p>
+ * The file contains {@link ProcessCommands#MAX_PROCESSES} groups of {@link #BYTE_LENGTH_FOR_ONE_PROCESS} bits.
+ * Each group of byte is used as follow:
+ * <ul>
+ * <li>First byte contains {@link #EMPTY} until process is UP and writes {@link #UP}</li>
+ * <li>Second byte contains {@link #EMPTY} until any process requests current one to stop by writing value {@link #STOP}</li>
+ * <li>Third byte contains {@link #EMPTY} until any process requests current one to restart by writing value {@link #RESTART}. Process acknowledges restart by writing back {@link #EMPTY}</li>
+ * <li>The next 8 bytes contains a long (value of {@link System#currentTimeMillis()}) which represents the date of the last ping</li>
+ * </ul>
+ * </p>
*/
public class AllProcessesCommands {
+ private static final int UP_BYTE_OFFSET = 0;
+ private static final int STOP_BYTE_OFFSET = 1;
+ private static final int RESTART_BYTE_OFFSET = 2;
+ private static final int PING_BYTE_OFFSET = 3;
- /**
- * The ByteBuffer will contains :
- * <ul>
- * <li>First byte will contains 0x00 until stop command is issued = 0xFF</li>
- * <li>Then each 10 bytes will be reserved for each process</li>
- * </ul>
- *
- * Description of ten bytes of each process :
- * <ul>
- * <li>First byte will contains the state 0x00 until READY 0x01</li>
- * <li>The second byte will contains the request for stopping 0x00 or STOP (0xFF)</li>
- * <li>The second byte will contains the request for restarting 0x00 or RESTART (0xAA)</li>
- * <li>The next 8 bytes contains a long (System.currentTimeInMillis for ping)</li>
- * </ul>
- */
- final MappedByteBuffer mappedByteBuffer;
- private final RandomAccessFile sharedMemory;
private static final int BYTE_LENGTH_FOR_ONE_PROCESS = 1 + 1 + 1 + 8;
// With this shared memory we can handle up to MAX_PROCESSES processes
private static final int MAX_SHARED_MEMORY = BYTE_LENGTH_FOR_ONE_PROCESS * MAX_PROCESSES;
- public static final byte STOP = (byte) 0xFF;
- public static final byte RESTART = (byte) 0xAA;
- public static final byte READY = (byte) 0x01;
- public static final byte EMPTY = (byte) 0x00;
+ private static final byte STOP = (byte) 0xFF;
+ private static final byte RESTART = (byte) 0xAA;
+ private static final byte UP = (byte) 0x01;
+ private static final byte EMPTY = (byte) 0x00;
+
+ //VisibleForTesting
+ final MappedByteBuffer mappedByteBuffer;
+ private final RandomAccessFile sharedMemory;
public AllProcessesCommands(File directory) {
if (!directory.isDirectory() || !directory.exists()) {
@@ -103,56 +107,52 @@ public class AllProcessesCommands {
return processCommands;
}
- boolean isReady(int processNumber) {
- return mappedByteBuffer.get(offset(processNumber)) == READY;
+ boolean isUp(int processNumber) {
+ return readByte(processNumber, UP_BYTE_OFFSET) == UP;
}
/**
- * To be executed by child process to declare that it's ready
+ * To be executed by child process to declare that it is done starting
*/
- void setReady(int processNumber) {
- mappedByteBuffer.put(offset(processNumber), READY);
+ void setUp(int processNumber) {
+ writeByte(processNumber, UP_BYTE_OFFSET, UP);
}
void ping(int processNumber) {
- mappedByteBuffer.putLong(2 + offset(processNumber), System.currentTimeMillis());
+ writeLong(processNumber, PING_BYTE_OFFSET, System.currentTimeMillis());
}
long getLastPing(int processNumber) {
- return mappedByteBuffer.getLong(2 + offset(processNumber));
+ return readLong(processNumber, PING_BYTE_OFFSET);
}
/**
* To be executed by monitor process to ask for child process termination
*/
void askForStop(int processNumber) {
- mappedByteBuffer.put(offset(processNumber) + 1, STOP);
+ writeByte(processNumber, STOP_BYTE_OFFSET, STOP);
}
boolean askedForStop(int processNumber) {
- return mappedByteBuffer.get(offset(processNumber) + 1) == STOP;
+ return readByte(processNumber, STOP_BYTE_OFFSET) == STOP;
}
void askForRestart(int processNumber) {
- mappedByteBuffer.put(offset(processNumber) + 3, RESTART);
+ writeByte(processNumber, RESTART_BYTE_OFFSET, RESTART);
}
boolean askedForRestart(int processNumber) {
- return mappedByteBuffer.get(offset(processNumber) + 3) == RESTART;
+ return readByte(processNumber, RESTART_BYTE_OFFSET) == RESTART;
}
void acknowledgeAskForRestart(int processNumber) {
- mappedByteBuffer.put(offset(processNumber) + 3, EMPTY);
+ writeByte(processNumber, RESTART_BYTE_OFFSET, EMPTY);
}
public void close() {
IOUtils.closeQuietly(sharedMemory);
}
- int offset(int processNumber) {
- return BYTE_LENGTH_FOR_ONE_PROCESS * processNumber;
- }
-
public void checkProcessNumber(int processNumber) {
boolean result = processNumber >= 0 && processNumber < MAX_PROCESSES;
if (!result) {
@@ -162,10 +162,31 @@ public class AllProcessesCommands {
private void cleanData(int processNumber) {
for (int i = 0; i < BYTE_LENGTH_FOR_ONE_PROCESS; i++) {
- mappedByteBuffer.put(offset(processNumber) + i, EMPTY);
+ writeByte(processNumber, i, EMPTY);
}
}
+ private void writeByte(int processNumber, int offset, byte value) {
+ mappedByteBuffer.put(offset(processNumber) + offset, value);
+ }
+
+ private byte readByte(int processNumber, int offset) {
+ return mappedByteBuffer.get(offset(processNumber) + offset);
+ }
+
+ private void writeLong(int processNumber, int offset, long value) {
+ mappedByteBuffer.putLong(offset(processNumber) + offset, value);
+ }
+
+ private long readLong(int processNumber, int offset) {
+ return mappedByteBuffer.getLong(offset(processNumber) + offset);
+ }
+
+ // VisibleForTesting
+ int offset(int processNumber) {
+ return BYTE_LENGTH_FOR_ONE_PROCESS * processNumber;
+ }
+
private class ProcessCommandsImpl implements ProcessCommands {
private final int processNumber;
@@ -175,13 +196,13 @@ public class AllProcessesCommands {
}
@Override
- public boolean isReady() {
- return AllProcessesCommands.this.isReady(processNumber);
+ public boolean isUp() {
+ return AllProcessesCommands.this.isUp(processNumber);
}
@Override
- public void setReady() {
- AllProcessesCommands.this.setReady(processNumber);
+ public void setUp() {
+ AllProcessesCommands.this.setUp(processNumber);
}
@Override
diff --git a/server/sonar-process/src/main/java/org/sonar/process/DefaultProcessCommands.java b/server/sonar-process/src/main/java/org/sonar/process/DefaultProcessCommands.java
index 68ca4a98063..0ef4413aa75 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/DefaultProcessCommands.java
+++ b/server/sonar-process/src/main/java/org/sonar/process/DefaultProcessCommands.java
@@ -40,13 +40,13 @@ public class DefaultProcessCommands implements ProcessCommands {
}
@Override
- public boolean isReady() {
- return delegate.isReady();
+ public boolean isUp() {
+ return delegate.isUp();
}
@Override
- public void setReady() {
- delegate.setReady();
+ public void setUp() {
+ delegate.setUp();
}
@Override
diff --git a/server/sonar-process/src/main/java/org/sonar/process/Monitored.java b/server/sonar-process/src/main/java/org/sonar/process/Monitored.java
index f7015ac42d8..87a6b1f32c1 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/Monitored.java
+++ b/server/sonar-process/src/main/java/org/sonar/process/Monitored.java
@@ -27,11 +27,10 @@ public interface Monitored {
void start();
/**
- * True if the process is started and operational (-> can accept requests), false if
- * it's still starting. An exception is thrown is process failed to start (not starting
- * nor started).
+ * True if the process is done starting, false otherwise.
+ * An exception may be thrown if process fails to start.
*/
- boolean isReady();
+ boolean isUp();
/**
* Blocks until the process is terminated
diff --git a/server/sonar-process/src/main/java/org/sonar/process/ProcessCommands.java b/server/sonar-process/src/main/java/org/sonar/process/ProcessCommands.java
index e1299f94092..f52f5b5f5df 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/ProcessCommands.java
+++ b/server/sonar-process/src/main/java/org/sonar/process/ProcessCommands.java
@@ -19,33 +19,27 @@
*/
package org.sonar.process;
+import java.io.File;
+
/**
* Process inter-communication to :
* <ul>
- * <li>share status of child process</li>
- * <li>stop child process</li>
- * <li>restart all child processes</li>
+ * <li>share status of specific process</li>
+ * <li>stop/restart a specific processes</li>
* </ul>
*
- * <p/>
- * It relies on files shared by both processes. Following alternatives were considered but not selected :
- * <ul>
- * <li>JMX beans over RMI: network issues (mostly because of Java reverse-DNS) + requires to configure and open a new port</li>
- * <li>simple socket protocol: same drawbacks are RMI connection</li>
- * <li>java.lang.Process#destroy(): shutdown hooks are not executed on some OS (mostly MSWindows)</li>
- * <li>execute OS-specific commands (for instance kill on *nix): OS-specific, so hell to support. Moreover how to get identify a process ?</li>
- * </ul>
+ * @see DefaultProcessCommands#DefaultProcessCommands(File, int)
*/
public interface ProcessCommands extends AutoCloseable {
int MAX_PROCESSES = 50;
- boolean isReady();
+ boolean isUp();
/**
- * To be executed by child process to declare that it's ready
+ * To be executed by child process to declare that it is done starting
*/
- void setReady();
+ void setUp();
void ping();
diff --git a/server/sonar-process/src/main/java/org/sonar/process/ProcessEntryPoint.java b/server/sonar-process/src/main/java/org/sonar/process/ProcessEntryPoint.java
index d932511ed73..8968210526d 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/ProcessEntryPoint.java
+++ b/server/sonar-process/src/main/java/org/sonar/process/ProcessEntryPoint.java
@@ -79,14 +79,14 @@ public class ProcessEntryPoint implements Stoppable {
stopWatcher.start();
monitored.start();
- boolean ready = false;
- while (!ready) {
- ready = monitored.isReady();
+ boolean up = false;
+ while (!up) {
+ up = monitored.isUp();
Thread.sleep(20L);
}
// notify monitor that process is ready
- commands.setReady();
+ commands.setUp();
if (lifecycle.tryToMoveTo(Lifecycle.State.STARTED)) {
monitored.awaitStop();
diff --git a/server/sonar-process/src/test/java/org/sonar/process/AllProcessesCommandsTest.java b/server/sonar-process/src/test/java/org/sonar/process/AllProcessesCommandsTest.java
index 625ab016e1d..ad92121b824 100644
--- a/server/sonar-process/src/test/java/org/sonar/process/AllProcessesCommandsTest.java
+++ b/server/sonar-process/src/test/java/org/sonar/process/AllProcessesCommandsTest.java
@@ -33,6 +33,10 @@ import static org.sonar.process.ProcessCommands.MAX_PROCESSES;
public class AllProcessesCommandsTest {
private static final int PROCESS_NUMBER = 1;
+ private static final byte STOP = (byte) 0xFF;
+ private static final byte RESTART = (byte) 0xAA;
+ private static final byte UP = (byte) 0x01;
+ private static final byte EMPTY = (byte) 0x00;
@Rule
public TemporaryFolder temp = new TemporaryFolder();
@@ -57,17 +61,17 @@ public class AllProcessesCommandsTest {
File dir = temp.newFolder();
AllProcessesCommands commands = new AllProcessesCommands(dir);
- assertThat(commands.isReady(PROCESS_NUMBER)).isFalse();
- assertThat(commands.mappedByteBuffer.get(commands.offset(PROCESS_NUMBER))).isEqualTo(AllProcessesCommands.EMPTY);
+ assertThat(commands.isUp(PROCESS_NUMBER)).isFalse();
+ assertThat(commands.mappedByteBuffer.get(commands.offset(PROCESS_NUMBER))).isEqualTo(EMPTY);
assertThat(commands.mappedByteBuffer.getLong(2 + commands.offset(PROCESS_NUMBER))).isEqualTo(0L);
- commands.setReady(PROCESS_NUMBER);
- assertThat(commands.isReady(PROCESS_NUMBER)).isTrue();
- assertThat(commands.mappedByteBuffer.get(commands.offset(PROCESS_NUMBER))).isEqualTo(AllProcessesCommands.READY);
+ commands.setUp(PROCESS_NUMBER);
+ assertThat(commands.isUp(PROCESS_NUMBER)).isTrue();
+ assertThat(commands.mappedByteBuffer.get(commands.offset(PROCESS_NUMBER))).isEqualTo(UP);
long currentTime = System.currentTimeMillis();
commands.ping(PROCESS_NUMBER);
- assertThat(commands.mappedByteBuffer.getLong(2 + commands.offset(PROCESS_NUMBER))).isGreaterThanOrEqualTo(currentTime);
+ assertThat(commands.mappedByteBuffer.getLong(3 + commands.offset(PROCESS_NUMBER))).isGreaterThanOrEqualTo(currentTime);
}
@Test
@@ -75,12 +79,12 @@ public class AllProcessesCommandsTest {
File dir = temp.newFolder();
AllProcessesCommands commands = new AllProcessesCommands(dir);
- assertThat(commands.mappedByteBuffer.get(commands.offset(PROCESS_NUMBER) + PROCESS_NUMBER)).isNotEqualTo(AllProcessesCommands.STOP);
+ assertThat(commands.mappedByteBuffer.get(commands.offset(PROCESS_NUMBER) + 1)).isNotEqualTo(STOP);
assertThat(commands.askedForStop(PROCESS_NUMBER)).isFalse();
commands.askForStop(PROCESS_NUMBER);
assertThat(commands.askedForStop(PROCESS_NUMBER)).isTrue();
- assertThat(commands.mappedByteBuffer.get(commands.offset(PROCESS_NUMBER) + PROCESS_NUMBER)).isEqualTo(AllProcessesCommands.STOP);
+ assertThat(commands.mappedByteBuffer.get(commands.offset(PROCESS_NUMBER) + 1)).isEqualTo(STOP);
}
@Test
@@ -88,12 +92,12 @@ public class AllProcessesCommandsTest {
File dir = temp.newFolder();
AllProcessesCommands commands = new AllProcessesCommands(dir);
- assertThat(commands.mappedByteBuffer.get(commands.offset(PROCESS_NUMBER) + 3)).isNotEqualTo(AllProcessesCommands.RESTART);
+ assertThat(commands.mappedByteBuffer.get(commands.offset(PROCESS_NUMBER) +2)).isNotEqualTo(RESTART);
assertThat(commands.askedForRestart(PROCESS_NUMBER)).isFalse();
commands.askForRestart(PROCESS_NUMBER);
assertThat(commands.askedForRestart(PROCESS_NUMBER)).isTrue();
- assertThat(commands.mappedByteBuffer.get(commands.offset(PROCESS_NUMBER) + 3)).isEqualTo(AllProcessesCommands.RESTART);
+ assertThat(commands.mappedByteBuffer.get(commands.offset(PROCESS_NUMBER) + 2)).isEqualTo(RESTART);
}
@Test
@@ -101,11 +105,11 @@ public class AllProcessesCommandsTest {
File dir = temp.newFolder();
AllProcessesCommands commands = new AllProcessesCommands(dir);
- assertThat(commands.mappedByteBuffer.get(commands.offset(PROCESS_NUMBER) + 3)).isNotEqualTo(AllProcessesCommands.RESTART);
+ assertThat(commands.mappedByteBuffer.get(commands.offset(PROCESS_NUMBER) + 2)).isNotEqualTo(RESTART);
assertThat(commands.askedForRestart(PROCESS_NUMBER)).isFalse();
commands.acknowledgeAskForRestart(PROCESS_NUMBER);
- assertThat(commands.mappedByteBuffer.get(commands.offset(PROCESS_NUMBER) + 3)).isNotEqualTo(AllProcessesCommands.RESTART);
+ assertThat(commands.mappedByteBuffer.get(commands.offset(PROCESS_NUMBER) + 2)).isNotEqualTo(RESTART);
assertThat(commands.askedForRestart(PROCESS_NUMBER)).isFalse();
}
@@ -117,10 +121,10 @@ public class AllProcessesCommandsTest {
commands.askForRestart(PROCESS_NUMBER);
assertThat(commands.askedForRestart(PROCESS_NUMBER)).isTrue();
- assertThat(commands.mappedByteBuffer.get(commands.offset(PROCESS_NUMBER) + 3)).isEqualTo(AllProcessesCommands.RESTART);
+ assertThat(commands.mappedByteBuffer.get(commands.offset(PROCESS_NUMBER) + 2)).isEqualTo(RESTART);
commands.acknowledgeAskForRestart(PROCESS_NUMBER);
- assertThat(commands.mappedByteBuffer.get(commands.offset(PROCESS_NUMBER) + 3)).isNotEqualTo(AllProcessesCommands.RESTART);
+ assertThat(commands.mappedByteBuffer.get(commands.offset(PROCESS_NUMBER) + 2)).isNotEqualTo(RESTART);
assertThat(commands.askedForRestart(PROCESS_NUMBER)).isFalse();
}
diff --git a/server/sonar-process/src/test/java/org/sonar/process/DefaultProcessCommandsTest.java b/server/sonar-process/src/test/java/org/sonar/process/DefaultProcessCommandsTest.java
index b641ddbba5c..89d07923427 100644
--- a/server/sonar-process/src/test/java/org/sonar/process/DefaultProcessCommandsTest.java
+++ b/server/sonar-process/src/test/java/org/sonar/process/DefaultProcessCommandsTest.java
@@ -57,10 +57,10 @@ public class DefaultProcessCommandsTest {
File dir = temp.newFolder();
DefaultProcessCommands commands = new DefaultProcessCommands(dir, PROCESS_NUMBER);
- assertThat(commands.isReady()).isFalse();
+ assertThat(commands.isUp()).isFalse();
- commands.setReady();
- assertThat(commands.isReady()).isTrue();
+ commands.setUp();
+ assertThat(commands.isUp()).isTrue();
}
@Test
diff --git a/server/sonar-process/src/test/java/org/sonar/process/ProcessEntryPointTest.java b/server/sonar-process/src/test/java/org/sonar/process/ProcessEntryPointTest.java
index e8a62fc58b0..8e107af3ee1 100644
--- a/server/sonar-process/src/test/java/org/sonar/process/ProcessEntryPointTest.java
+++ b/server/sonar-process/src/test/java/org/sonar/process/ProcessEntryPointTest.java
@@ -165,7 +165,7 @@ public class ProcessEntryPointTest {
}
@Override
- public boolean isReady() {
+ public boolean isUp() {
return true;
}
@@ -188,7 +188,7 @@ public class ProcessEntryPointTest {
}
@Override
- public boolean isReady() {
+ public boolean isUp() {
return false;
}
diff --git a/server/sonar-process/src/test/java/org/sonar/process/test/HttpProcess.java b/server/sonar-process/src/test/java/org/sonar/process/test/HttpProcess.java
index 60cb57644f4..77e135163ef 100644
--- a/server/sonar-process/src/test/java/org/sonar/process/test/HttpProcess.java
+++ b/server/sonar-process/src/test/java/org/sonar/process/test/HttpProcess.java
@@ -88,7 +88,7 @@ public class HttpProcess implements Monitored {
}
@Override
- public boolean isReady() {
+ public boolean isUp() {
if (ready) {
return true;
}
diff --git a/server/sonar-process/src/test/java/org/sonar/process/test/InfiniteTerminationProcess.java b/server/sonar-process/src/test/java/org/sonar/process/test/InfiniteTerminationProcess.java
index cb75808346f..3cf328eff4d 100644
--- a/server/sonar-process/src/test/java/org/sonar/process/test/InfiniteTerminationProcess.java
+++ b/server/sonar-process/src/test/java/org/sonar/process/test/InfiniteTerminationProcess.java
@@ -51,7 +51,7 @@ public class InfiniteTerminationProcess implements Monitored {
}
@Override
- public boolean isReady() {
+ public boolean isUp() {
return state == State.STARTED;
}
diff --git a/server/sonar-process/src/test/java/org/sonar/process/test/StandardProcess.java b/server/sonar-process/src/test/java/org/sonar/process/test/StandardProcess.java
index b3b3a5099cb..9e9ab0485e9 100644
--- a/server/sonar-process/src/test/java/org/sonar/process/test/StandardProcess.java
+++ b/server/sonar-process/src/test/java/org/sonar/process/test/StandardProcess.java
@@ -51,7 +51,7 @@ public class StandardProcess implements Monitored {
}
@Override
- public boolean isReady() {
+ public boolean isUp() {
return state == State.STARTED;
}