diff options
Diffstat (limited to 'server/sonar-process')
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; } |