From: Sébastien Lesaint Date: Fri, 18 Aug 2017 07:51:33 +0000 (+0200) Subject: SONAR-9590 move shared memory file classes to dedicated package X-Git-Tag: 6.6-RC1~474 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=dd056cf31ff9cbd6b230cf824227fbb26ee29bc8;p=sonarqube.git SONAR-9590 move shared memory file classes to dedicated package --- diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/httpd/CeHttpServer.java b/server/sonar-ce/src/main/java/org/sonar/ce/httpd/CeHttpServer.java index 3a8f28fb4a4..bf92cfb6ec0 100644 --- a/server/sonar-ce/src/main/java/org/sonar/ce/httpd/CeHttpServer.java +++ b/server/sonar-ce/src/main/java/org/sonar/ce/httpd/CeHttpServer.java @@ -30,7 +30,7 @@ import java.util.Map; import java.util.Optional; import java.util.Properties; import org.slf4j.LoggerFactory; -import org.sonar.process.DefaultProcessCommands; +import org.sonar.process.sharedmemoryfile.DefaultProcessCommands; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; diff --git a/server/sonar-ce/src/test/java/org/sonar/ce/httpd/CeHttpServerTest.java b/server/sonar-ce/src/test/java/org/sonar/ce/httpd/CeHttpServerTest.java index cb1d68eadd5..8cc7a8c38ec 100644 --- a/server/sonar-ce/src/test/java/org/sonar/ce/httpd/CeHttpServerTest.java +++ b/server/sonar-ce/src/test/java/org/sonar/ce/httpd/CeHttpServerTest.java @@ -35,7 +35,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; -import org.sonar.process.DefaultProcessCommands; +import org.sonar.process.sharedmemoryfile.DefaultProcessCommands; import static org.assertj.core.api.Assertions.assertThat; import static org.sonar.process.ProcessEntryPoint.PROPERTY_PROCESS_INDEX; diff --git a/server/sonar-process-monitor/src/main/java/org/sonar/application/AppFileSystem.java b/server/sonar-process-monitor/src/main/java/org/sonar/application/AppFileSystem.java index 99d22bd26f3..f397d608d55 100644 --- a/server/sonar-process-monitor/src/main/java/org/sonar/application/AppFileSystem.java +++ b/server/sonar-process-monitor/src/main/java/org/sonar/application/AppFileSystem.java @@ -32,7 +32,7 @@ import java.util.EnumSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.application.config.AppSettings; -import org.sonar.process.AllProcessesCommands; +import org.sonar.process.sharedmemoryfile.AllProcessesCommands; import static java.lang.String.format; import static java.nio.file.FileVisitResult.CONTINUE; diff --git a/server/sonar-process-monitor/src/main/java/org/sonar/application/process/ProcessCommandsProcessMonitor.java b/server/sonar-process-monitor/src/main/java/org/sonar/application/process/ProcessCommandsProcessMonitor.java index 77ebdeeb521..672debcadc0 100644 --- a/server/sonar-process-monitor/src/main/java/org/sonar/application/process/ProcessCommandsProcessMonitor.java +++ b/server/sonar-process-monitor/src/main/java/org/sonar/application/process/ProcessCommandsProcessMonitor.java @@ -19,7 +19,7 @@ */ package org.sonar.application.process; -import org.sonar.process.ProcessCommands; +import org.sonar.process.sharedmemoryfile.ProcessCommands; import org.sonar.process.ProcessId; import static java.util.Objects.requireNonNull; diff --git a/server/sonar-process-monitor/src/main/java/org/sonar/application/process/ProcessLauncherImpl.java b/server/sonar-process-monitor/src/main/java/org/sonar/application/process/ProcessLauncherImpl.java index 19b8eb6504f..4bb42a72f45 100644 --- a/server/sonar-process-monitor/src/main/java/org/sonar/application/process/ProcessLauncherImpl.java +++ b/server/sonar-process-monitor/src/main/java/org/sonar/application/process/ProcessLauncherImpl.java @@ -35,8 +35,8 @@ import java.util.stream.Collectors; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.sonar.process.AllProcessesCommands; -import org.sonar.process.ProcessCommands; +import org.sonar.process.sharedmemoryfile.AllProcessesCommands; +import org.sonar.process.sharedmemoryfile.ProcessCommands; import org.sonar.process.ProcessId; import static java.lang.String.format; @@ -47,7 +47,7 @@ import static org.sonar.process.ProcessEntryPoint.PROPERTY_TERMINATION_TIMEOUT; public class ProcessLauncherImpl implements ProcessLauncher { private static final Logger LOG = LoggerFactory.getLogger(ProcessLauncherImpl.class); - public static final String ELASTICSEARCH_JVM_OPTIONS_HEADER = "# This file has been automatically generated by SonarQube during startup.\n" + + private static final String ELASTICSEARCH_JVM_OPTIONS_HEADER = "# This file has been automatically generated by SonarQube during startup.\n" + "# Please use the sonar.search.javaOpts in sonar.properties to specify jvm options for Elasticsearch\n" + "\n" + "# DO NOT EDIT THIS FILE\n" + diff --git a/server/sonar-process-monitor/src/main/java/org/sonar/application/process/StopRequestWatcherImpl.java b/server/sonar-process-monitor/src/main/java/org/sonar/application/process/StopRequestWatcherImpl.java index 6c92a14a2e2..fc042f614c3 100644 --- a/server/sonar-process-monitor/src/main/java/org/sonar/application/process/StopRequestWatcherImpl.java +++ b/server/sonar-process-monitor/src/main/java/org/sonar/application/process/StopRequestWatcherImpl.java @@ -22,8 +22,8 @@ package org.sonar.application.process; import org.sonar.application.FileSystem; import org.sonar.application.Scheduler; import org.sonar.application.config.AppSettings; -import org.sonar.process.DefaultProcessCommands; -import org.sonar.process.ProcessCommands; +import org.sonar.process.sharedmemoryfile.DefaultProcessCommands; +import org.sonar.process.sharedmemoryfile.ProcessCommands; import org.sonar.process.ProcessId; import org.sonar.process.ProcessProperties; diff --git a/server/sonar-process-monitor/src/test/java/org/sonar/application/AppFileSystemTest.java b/server/sonar-process-monitor/src/test/java/org/sonar/application/AppFileSystemTest.java index 16c11d89946..3786190fb37 100644 --- a/server/sonar-process-monitor/src/test/java/org/sonar/application/AppFileSystemTest.java +++ b/server/sonar-process-monitor/src/test/java/org/sonar/application/AppFileSystemTest.java @@ -33,11 +33,11 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.sonar.application.config.TestAppSettings; -import org.sonar.process.AllProcessesCommands; +import org.sonar.process.sharedmemoryfile.AllProcessesCommands; import org.sonar.process.ProcessProperties; import static org.assertj.core.api.Assertions.assertThat; -import static org.sonar.process.ProcessCommands.MAX_PROCESSES; +import static org.sonar.process.sharedmemoryfile.ProcessCommands.MAX_PROCESSES; public class AppFileSystemTest { diff --git a/server/sonar-process-monitor/src/test/java/org/sonar/application/process/ProcessCommandsProcessMonitorTest.java b/server/sonar-process-monitor/src/test/java/org/sonar/application/process/ProcessCommandsProcessMonitorTest.java index c7a779bcb61..66c60fc73ee 100644 --- a/server/sonar-process-monitor/src/test/java/org/sonar/application/process/ProcessCommandsProcessMonitorTest.java +++ b/server/sonar-process-monitor/src/test/java/org/sonar/application/process/ProcessCommandsProcessMonitorTest.java @@ -24,7 +24,7 @@ import java.io.InputStream; import java.util.concurrent.TimeUnit; import org.junit.Test; import org.mockito.Mockito; -import org.sonar.process.ProcessCommands; +import org.sonar.process.sharedmemoryfile.ProcessCommands; import org.sonar.process.ProcessId; import static org.assertj.core.api.Assertions.assertThat; diff --git a/server/sonar-process-monitor/src/test/java/org/sonar/application/process/ProcessLauncherImplTest.java b/server/sonar-process-monitor/src/test/java/org/sonar/application/process/ProcessLauncherImplTest.java index c7d55a92b57..d45927b589f 100644 --- a/server/sonar-process-monitor/src/test/java/org/sonar/application/process/ProcessLauncherImplTest.java +++ b/server/sonar-process-monitor/src/test/java/org/sonar/application/process/ProcessLauncherImplTest.java @@ -30,7 +30,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; -import org.sonar.process.AllProcessesCommands; +import org.sonar.process.sharedmemoryfile.AllProcessesCommands; import org.sonar.process.ProcessId; import static org.assertj.core.api.Assertions.assertThat; diff --git a/server/sonar-process-monitor/src/test/java/org/sonar/application/process/StopRequestWatcherImplTest.java b/server/sonar-process-monitor/src/test/java/org/sonar/application/process/StopRequestWatcherImplTest.java index 2ac305797e4..6d0b9725453 100644 --- a/server/sonar-process-monitor/src/test/java/org/sonar/application/process/StopRequestWatcherImplTest.java +++ b/server/sonar-process-monitor/src/test/java/org/sonar/application/process/StopRequestWatcherImplTest.java @@ -29,7 +29,7 @@ import org.junit.rules.Timeout; import org.sonar.application.FileSystem; import org.sonar.application.Scheduler; import org.sonar.application.config.AppSettings; -import org.sonar.process.ProcessCommands; +import org.sonar.process.sharedmemoryfile.ProcessCommands; import org.sonar.process.ProcessProperties; import static org.assertj.core.api.Assertions.assertThat; 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 deleted file mode 100644 index ea4b6ae2b3b..00000000000 --- a/server/sonar-process/src/main/java/org/sonar/process/AllProcessesCommands.java +++ /dev/null @@ -1,330 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * 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 02110-1301, USA. - */ -package org.sonar.process; - -import java.io.File; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.charset.StandardCharsets; -import org.apache.commons.io.IOUtils; - -import static java.lang.String.format; -import static org.apache.commons.lang.StringUtils.rightPad; -import static org.sonar.process.ProcessCommands.MAX_PROCESSES; - -/** - * Process inter-communication to : - * - * - *

- * It relies on a single file accessed by all processes through a {@link MappedByteBuffer}.
- * Following alternatives were considered but not selected : - *

- *

- * - *

- * The file contains {@link ProcessCommands#MAX_PROCESSES} groups of {@link #BYTE_LENGTH_FOR_ONE_PROCESS} bits. - * Each group of byte is used as follow: - *

- *

- */ -public class AllProcessesCommands implements AutoCloseable { - 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 OPERATIONAL_BYTE_OFFSET = 3; - private static final int PING_BYTE_OFFSET = 4; - private static final int SYSTEM_INFO_URL_BYTE_OFFSET = PING_BYTE_OFFSET + 8; - - private static final int SYSTEM_INFO_URL_SIZE_IN_BYTES = 500; - - private static final int BYTE_LENGTH_FOR_ONE_PROCESS = 1 + 1 + 1 + 1 + 8 + SYSTEM_INFO_URL_SIZE_IN_BYTES; - - // 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; - - private static final byte STOP = (byte) 0xFF; - private static final byte RESTART = (byte) 0xAA; - private static final byte OPERATIONAL = (byte) 0x59; - 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()) { - throw new IllegalArgumentException("Not a valid directory: " + directory); - } - - try { - sharedMemory = new RandomAccessFile(new File(directory, "sharedmemory"), "rw"); - mappedByteBuffer = sharedMemory.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, MAX_SHARED_MEMORY); - } catch (IOException e) { - throw new IllegalArgumentException("Unable to create shared memory : ", e); - } - } - - public void clean() { - for (int i = 0; i < MAX_PROCESSES; i++) { - cleanData(i); - } - } - - public ProcessCommands create(int processNumber) { - return createForProcess(processNumber, false); - } - - public ProcessCommands createAfterClean(int processNumber) { - return createForProcess(processNumber, true); - } - - private ProcessCommands createForProcess(int processNumber, boolean clean) { - checkProcessNumber(processNumber); - ProcessCommands processCommands = new ProcessCommandsImpl(processNumber); - if (clean) { - cleanData(processNumber); - } - return processCommands; - } - - boolean isUp(int processNumber) { - return readByte(processNumber, UP_BYTE_OFFSET) == UP; - } - - /** - * To be executed by child process to declare that it is done starting - */ - void setUp(int processNumber) { - writeByte(processNumber, UP_BYTE_OFFSET, UP); - } - - boolean isOperational(int processNumber) { - return readByte(processNumber, OPERATIONAL_BYTE_OFFSET) == OPERATIONAL; - } - - /** - * To be executed by child process to declare that it is started and fully operational - */ - void setOperational(int processNumber) { - writeByte(processNumber, OPERATIONAL_BYTE_OFFSET, OPERATIONAL); - } - - void ping(int processNumber) { - writeLong(processNumber, PING_BYTE_OFFSET, System.currentTimeMillis()); - } - - long getLastPing(int processNumber) { - return readLong(processNumber, PING_BYTE_OFFSET); - } - - String getSystemInfoUrl(int processNumber) { - byte[] urlBytes = readBytes(processNumber, SYSTEM_INFO_URL_BYTE_OFFSET, SYSTEM_INFO_URL_SIZE_IN_BYTES); - return new String(urlBytes, StandardCharsets.US_ASCII).trim(); - } - - void setSystemInfoUrl(int processNumber, String url) { - byte[] urlBytes = rightPad(url, SYSTEM_INFO_URL_SIZE_IN_BYTES).getBytes(StandardCharsets.US_ASCII); - if (urlBytes.length > SYSTEM_INFO_URL_SIZE_IN_BYTES) { - throw new IllegalArgumentException(format("System Info URL is too long. Max is %d bytes. Got: %s", SYSTEM_INFO_URL_SIZE_IN_BYTES, url)); - } - writeBytes(processNumber, SYSTEM_INFO_URL_BYTE_OFFSET, urlBytes); - } - - /** - * To be executed by monitor process to ask for child process termination - */ - void askForStop(int processNumber) { - writeByte(processNumber, STOP_BYTE_OFFSET, STOP); - } - - boolean askedForStop(int processNumber) { - return readByte(processNumber, STOP_BYTE_OFFSET) == STOP; - } - - void askForRestart(int processNumber) { - writeByte(processNumber, RESTART_BYTE_OFFSET, RESTART); - } - - boolean askedForRestart(int processNumber) { - return readByte(processNumber, RESTART_BYTE_OFFSET) == RESTART; - } - - void acknowledgeAskForRestart(int processNumber) { - writeByte(processNumber, RESTART_BYTE_OFFSET, EMPTY); - } - - @Override - public void close() { - IOUtils.closeQuietly(sharedMemory); - } - - public void checkProcessNumber(int processNumber) { - if (processNumber < 0 || processNumber >= MAX_PROCESSES) { - throw new IllegalArgumentException(format("Process number %s is not valid", processNumber)); - } - } - - private void cleanData(int processNumber) { - for (int i = 0; i < BYTE_LENGTH_FOR_ONE_PROCESS; i++) { - writeByte(processNumber, i, EMPTY); - } - } - - private void writeByte(int processNumber, int offset, byte value) { - mappedByteBuffer.put(offset(processNumber) + offset, value); - } - - private void writeBytes(int processNumber, int offset, byte[] value) { - int bufferOffset = offset(processNumber) + offset; - for (int i = 0; i < value.length; i++) { - mappedByteBuffer.put(bufferOffset + i, value[i]); - } - } - - private byte readByte(int processNumber, int offset) { - return mappedByteBuffer.get(offset(processNumber) + offset); - } - - private byte[] readBytes(int processNumber, int offset, int length) { - int bufferOffset = offset(processNumber) + offset; - byte[] bytes = new byte[length]; - for (int i = 0; i < length; i++) { - bytes[i] = mappedByteBuffer.get(bufferOffset + i); - } - return bytes; - } - - 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; - - public ProcessCommandsImpl(int processNumber) { - this.processNumber = processNumber; - } - - @Override - public boolean isUp() { - return AllProcessesCommands.this.isUp(processNumber); - } - - @Override - public void setUp() { - AllProcessesCommands.this.setUp(processNumber); - } - - @Override - public boolean isOperational() { - return AllProcessesCommands.this.isOperational(processNumber); - } - - @Override - public void setOperational() { - AllProcessesCommands.this.setOperational(processNumber); - } - - @Override - public void ping() { - AllProcessesCommands.this.ping(processNumber); - } - - @Override - public long getLastPing() { - return AllProcessesCommands.this.getLastPing(processNumber); - } - - @Override - public void setHttpUrl(String s) { - AllProcessesCommands.this.setSystemInfoUrl(processNumber, s); - } - - @Override - public String getHttpUrl() { - return AllProcessesCommands.this.getSystemInfoUrl(processNumber); - } - - @Override - public void askForStop() { - AllProcessesCommands.this.askForStop(processNumber); - } - - @Override - public boolean askedForStop() { - return AllProcessesCommands.this.askedForStop(processNumber); - } - - @Override - public void askForRestart() { - AllProcessesCommands.this.askForRestart(processNumber); - } - - @Override - public boolean askedForRestart() { - return AllProcessesCommands.this.askedForRestart(processNumber); - } - - @Override - public void acknowledgeAskForRestart() { - AllProcessesCommands.this.acknowledgeAskForRestart(processNumber); - } - - @Override - public void endWatch() { - throw new UnsupportedOperationException("ProcessCommands created from AllProcessesCommands can not be closed directly. Close AllProcessesCommands instead"); - } - - @Override - public void close() { - endWatch(); - } - } -} 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 deleted file mode 100644 index 8c29a972f70..00000000000 --- a/server/sonar-process/src/main/java/org/sonar/process/DefaultProcessCommands.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * 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 02110-1301, USA. - */ -package org.sonar.process; - -import java.io.File; -import org.slf4j.LoggerFactory; - -/** - * Default implementation of {@link ProcessCommands} based on a {@link AllProcessesCommands} of which will request a - * single {@link ProcessCommands} to use as delegate for the specified processNumber. - */ -public class DefaultProcessCommands implements ProcessCommands { - private final AllProcessesCommands allProcessesCommands; - private final ProcessCommands delegate; - - private DefaultProcessCommands(File directory, int processNumber, boolean clean) { - this.allProcessesCommands = new AllProcessesCommands(directory); - this.delegate = clean ? allProcessesCommands.createAfterClean(processNumber) : allProcessesCommands.create(processNumber); - } - - /** - * Main DefaultProcessCommands will clear the shared memory space of the specified process number when created and will - * then write and/or read to it. - * Therefor there should be only one main DefaultProcessCommands. - */ - public static DefaultProcessCommands main(File directory, int processNumber) { - return new DefaultProcessCommands(directory, processNumber, true); - } - - /** - * Secondary DefaultProcessCommands will read and write to the shared memory space but will not clear it. Therefor, there - * can be any number of them. - */ - public static DefaultProcessCommands secondary(File directory, int processNumber) { - return new DefaultProcessCommands(directory, processNumber, false); - } - - /** - * Clears the shared memory space of the specified process number. - */ - public static void reset(File directory, int processNumber) { - try (DefaultProcessCommands processCommands = new DefaultProcessCommands(directory, processNumber, true)) { - // nothing else to do than open file and reset the space of specified process - } - } - - @Override - public boolean isUp() { - return delegate.isUp(); - } - - @Override - public void setUp() { - delegate.setUp(); - } - - @Override - public boolean isOperational() { - return delegate.isOperational(); - } - - @Override - public void setOperational() { - delegate.setOperational(); - } - - @Override - public void ping() { - delegate.ping(); - } - - @Override - public long getLastPing() { - return delegate.getLastPing(); - } - - @Override - public void setHttpUrl(String s) { - delegate.setHttpUrl(s); - } - - @Override - public String getHttpUrl() { - return delegate.getHttpUrl(); - } - - @Override - public void askForStop() { - delegate.askForStop(); - } - - @Override - public boolean askedForStop() { - return delegate.askedForStop(); - } - - @Override - public void askForRestart() { - delegate.askForRestart(); - } - - @Override - public boolean askedForRestart() { - return delegate.askedForRestart(); - } - - @Override - public void acknowledgeAskForRestart() { - delegate.acknowledgeAskForRestart(); - } - - @Override - public void endWatch() { - try { - close(); - } catch (Exception e) { - LoggerFactory.getLogger(getClass()).error("Failed to close DefaultProcessCommands", e); - } - } - - @Override - public void close() { - allProcessesCommands.close(); - } -} 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 deleted file mode 100644 index 6ec0bf16736..00000000000 --- a/server/sonar-process/src/main/java/org/sonar/process/ProcessCommands.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * 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 02110-1301, USA. - */ -package org.sonar.process; - -import java.io.File; - -/** - * Process inter-communication to : - * - * - * @see DefaultProcessCommands#main(File, int) - * @see DefaultProcessCommands#secondary(File, int) - */ -public interface ProcessCommands extends AutoCloseable { - - int MAX_PROCESSES = 5; - - boolean isUp(); - - /** - * To be executed by child process to declare that it is done starting - */ - void setUp(); - - boolean isOperational(); - - /** - * To be executed by child process to declare that it is done starting and fully operational. - * - * @throws IllegalStateException if {@link #setUp()} has not been called - */ - void setOperational(); - - void ping(); - - long getLastPing(); - - void setHttpUrl(String s); - - String getHttpUrl(); - - /** - * To be executed by monitor process to ask for child process termination - */ - void askForStop(); - - boolean askedForStop(); - - /** - * To be executed by child process to ask for restart of all child processes - */ - void askForRestart(); - - /** - * Can be called by child or monitor process to know whether child process asked for restart - */ - boolean askedForRestart(); - - /** - * To be executed by monitor process to acknowledge restart request from child process. - */ - void acknowledgeAskForRestart(); - - void endWatch(); -} 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 15190a41dcc..b1476fba53e 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 @@ -22,6 +22,8 @@ package org.sonar.process; import java.io.File; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.sonar.process.sharedmemoryfile.DefaultProcessCommands; +import org.sonar.process.sharedmemoryfile.ProcessCommands; public class ProcessEntryPoint implements Stoppable { diff --git a/server/sonar-process/src/main/java/org/sonar/process/StopWatcher.java b/server/sonar-process/src/main/java/org/sonar/process/StopWatcher.java index 0043bccf239..1739dc0423f 100644 --- a/server/sonar-process/src/main/java/org/sonar/process/StopWatcher.java +++ b/server/sonar-process/src/main/java/org/sonar/process/StopWatcher.java @@ -20,6 +20,7 @@ package org.sonar.process; import org.slf4j.LoggerFactory; +import org.sonar.process.sharedmemoryfile.ProcessCommands; /** * This watchdog asks for graceful termination of process when the file diff --git a/server/sonar-process/src/main/java/org/sonar/process/StopperThread.java b/server/sonar-process/src/main/java/org/sonar/process/StopperThread.java index f537d204bcb..746cd31dc1e 100644 --- a/server/sonar-process/src/main/java/org/sonar/process/StopperThread.java +++ b/server/sonar-process/src/main/java/org/sonar/process/StopperThread.java @@ -25,6 +25,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import org.sonar.process.sharedmemoryfile.ProcessCommands; /** * Gracefully stops process in a timely fashion diff --git a/server/sonar-process/src/main/java/org/sonar/process/sharedmemoryfile/AllProcessesCommands.java b/server/sonar-process/src/main/java/org/sonar/process/sharedmemoryfile/AllProcessesCommands.java new file mode 100644 index 00000000000..c5d7ec46f5e --- /dev/null +++ b/server/sonar-process/src/main/java/org/sonar/process/sharedmemoryfile/AllProcessesCommands.java @@ -0,0 +1,330 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * 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 02110-1301, USA. + */ +package org.sonar.process.sharedmemoryfile; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.charset.StandardCharsets; +import org.apache.commons.io.IOUtils; + +import static java.lang.String.format; +import static org.apache.commons.lang.StringUtils.rightPad; +import static org.sonar.process.sharedmemoryfile.ProcessCommands.MAX_PROCESSES; + +/** + * Process inter-communication to : + * + * + *

+ * It relies on a single file accessed by all processes through a {@link MappedByteBuffer}.
+ * Following alternatives were considered but not selected : + *

+ *

+ * + *

+ * The file contains {@link ProcessCommands#MAX_PROCESSES} groups of {@link #BYTE_LENGTH_FOR_ONE_PROCESS} bits. + * Each group of byte is used as follow: + *

+ *

+ */ +public class AllProcessesCommands implements AutoCloseable { + 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 OPERATIONAL_BYTE_OFFSET = 3; + private static final int PING_BYTE_OFFSET = 4; + private static final int SYSTEM_INFO_URL_BYTE_OFFSET = PING_BYTE_OFFSET + 8; + + private static final int SYSTEM_INFO_URL_SIZE_IN_BYTES = 500; + + private static final int BYTE_LENGTH_FOR_ONE_PROCESS = 1 + 1 + 1 + 1 + 8 + SYSTEM_INFO_URL_SIZE_IN_BYTES; + + // 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; + + private static final byte STOP = (byte) 0xFF; + private static final byte RESTART = (byte) 0xAA; + private static final byte OPERATIONAL = (byte) 0x59; + 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()) { + throw new IllegalArgumentException("Not a valid directory: " + directory); + } + + try { + sharedMemory = new RandomAccessFile(new File(directory, "sharedmemory"), "rw"); + mappedByteBuffer = sharedMemory.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, MAX_SHARED_MEMORY); + } catch (IOException e) { + throw new IllegalArgumentException("Unable to create shared memory : ", e); + } + } + + public void clean() { + for (int i = 0; i < MAX_PROCESSES; i++) { + cleanData(i); + } + } + + public ProcessCommands create(int processNumber) { + return createForProcess(processNumber, false); + } + + public ProcessCommands createAfterClean(int processNumber) { + return createForProcess(processNumber, true); + } + + private ProcessCommands createForProcess(int processNumber, boolean clean) { + checkProcessNumber(processNumber); + ProcessCommands processCommands = new ProcessCommandsImpl(processNumber); + if (clean) { + cleanData(processNumber); + } + return processCommands; + } + + boolean isUp(int processNumber) { + return readByte(processNumber, UP_BYTE_OFFSET) == UP; + } + + /** + * To be executed by child process to declare that it is done starting + */ + void setUp(int processNumber) { + writeByte(processNumber, UP_BYTE_OFFSET, UP); + } + + boolean isOperational(int processNumber) { + return readByte(processNumber, OPERATIONAL_BYTE_OFFSET) == OPERATIONAL; + } + + /** + * To be executed by child process to declare that it is started and fully operational + */ + void setOperational(int processNumber) { + writeByte(processNumber, OPERATIONAL_BYTE_OFFSET, OPERATIONAL); + } + + void ping(int processNumber) { + writeLong(processNumber, PING_BYTE_OFFSET, System.currentTimeMillis()); + } + + long getLastPing(int processNumber) { + return readLong(processNumber, PING_BYTE_OFFSET); + } + + String getSystemInfoUrl(int processNumber) { + byte[] urlBytes = readBytes(processNumber, SYSTEM_INFO_URL_BYTE_OFFSET, SYSTEM_INFO_URL_SIZE_IN_BYTES); + return new String(urlBytes, StandardCharsets.US_ASCII).trim(); + } + + void setSystemInfoUrl(int processNumber, String url) { + byte[] urlBytes = rightPad(url, SYSTEM_INFO_URL_SIZE_IN_BYTES).getBytes(StandardCharsets.US_ASCII); + if (urlBytes.length > SYSTEM_INFO_URL_SIZE_IN_BYTES) { + throw new IllegalArgumentException(format("System Info URL is too long. Max is %d bytes. Got: %s", SYSTEM_INFO_URL_SIZE_IN_BYTES, url)); + } + writeBytes(processNumber, SYSTEM_INFO_URL_BYTE_OFFSET, urlBytes); + } + + /** + * To be executed by monitor process to ask for child process termination + */ + void askForStop(int processNumber) { + writeByte(processNumber, STOP_BYTE_OFFSET, STOP); + } + + boolean askedForStop(int processNumber) { + return readByte(processNumber, STOP_BYTE_OFFSET) == STOP; + } + + void askForRestart(int processNumber) { + writeByte(processNumber, RESTART_BYTE_OFFSET, RESTART); + } + + boolean askedForRestart(int processNumber) { + return readByte(processNumber, RESTART_BYTE_OFFSET) == RESTART; + } + + void acknowledgeAskForRestart(int processNumber) { + writeByte(processNumber, RESTART_BYTE_OFFSET, EMPTY); + } + + @Override + public void close() { + IOUtils.closeQuietly(sharedMemory); + } + + public void checkProcessNumber(int processNumber) { + if (processNumber < 0 || processNumber >= MAX_PROCESSES) { + throw new IllegalArgumentException(format("Process number %s is not valid", processNumber)); + } + } + + private void cleanData(int processNumber) { + for (int i = 0; i < BYTE_LENGTH_FOR_ONE_PROCESS; i++) { + writeByte(processNumber, i, EMPTY); + } + } + + private void writeByte(int processNumber, int offset, byte value) { + mappedByteBuffer.put(offset(processNumber) + offset, value); + } + + private void writeBytes(int processNumber, int offset, byte[] value) { + int bufferOffset = offset(processNumber) + offset; + for (int i = 0; i < value.length; i++) { + mappedByteBuffer.put(bufferOffset + i, value[i]); + } + } + + private byte readByte(int processNumber, int offset) { + return mappedByteBuffer.get(offset(processNumber) + offset); + } + + private byte[] readBytes(int processNumber, int offset, int length) { + int bufferOffset = offset(processNumber) + offset; + byte[] bytes = new byte[length]; + for (int i = 0; i < length; i++) { + bytes[i] = mappedByteBuffer.get(bufferOffset + i); + } + return bytes; + } + + 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; + + public ProcessCommandsImpl(int processNumber) { + this.processNumber = processNumber; + } + + @Override + public boolean isUp() { + return AllProcessesCommands.this.isUp(processNumber); + } + + @Override + public void setUp() { + AllProcessesCommands.this.setUp(processNumber); + } + + @Override + public boolean isOperational() { + return AllProcessesCommands.this.isOperational(processNumber); + } + + @Override + public void setOperational() { + AllProcessesCommands.this.setOperational(processNumber); + } + + @Override + public void ping() { + AllProcessesCommands.this.ping(processNumber); + } + + @Override + public long getLastPing() { + return AllProcessesCommands.this.getLastPing(processNumber); + } + + @Override + public void setHttpUrl(String s) { + AllProcessesCommands.this.setSystemInfoUrl(processNumber, s); + } + + @Override + public String getHttpUrl() { + return AllProcessesCommands.this.getSystemInfoUrl(processNumber); + } + + @Override + public void askForStop() { + AllProcessesCommands.this.askForStop(processNumber); + } + + @Override + public boolean askedForStop() { + return AllProcessesCommands.this.askedForStop(processNumber); + } + + @Override + public void askForRestart() { + AllProcessesCommands.this.askForRestart(processNumber); + } + + @Override + public boolean askedForRestart() { + return AllProcessesCommands.this.askedForRestart(processNumber); + } + + @Override + public void acknowledgeAskForRestart() { + AllProcessesCommands.this.acknowledgeAskForRestart(processNumber); + } + + @Override + public void endWatch() { + throw new UnsupportedOperationException("ProcessCommands created from AllProcessesCommands can not be closed directly. Close AllProcessesCommands instead"); + } + + @Override + public void close() { + endWatch(); + } + } +} diff --git a/server/sonar-process/src/main/java/org/sonar/process/sharedmemoryfile/DefaultProcessCommands.java b/server/sonar-process/src/main/java/org/sonar/process/sharedmemoryfile/DefaultProcessCommands.java new file mode 100644 index 00000000000..882a3eae37f --- /dev/null +++ b/server/sonar-process/src/main/java/org/sonar/process/sharedmemoryfile/DefaultProcessCommands.java @@ -0,0 +1,142 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * 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 02110-1301, USA. + */ +package org.sonar.process.sharedmemoryfile; + +import java.io.File; +import org.slf4j.LoggerFactory; + +/** + * Default implementation of {@link ProcessCommands} based on a {@link AllProcessesCommands} of which will request a + * single {@link ProcessCommands} to use as delegate for the specified processNumber. + */ +public class DefaultProcessCommands implements ProcessCommands { + private final AllProcessesCommands allProcessesCommands; + private final ProcessCommands delegate; + + private DefaultProcessCommands(File directory, int processNumber, boolean clean) { + this.allProcessesCommands = new AllProcessesCommands(directory); + this.delegate = clean ? allProcessesCommands.createAfterClean(processNumber) : allProcessesCommands.create(processNumber); + } + + /** + * Main DefaultProcessCommands will clear the shared memory space of the specified process number when created and will + * then write and/or read to it. + * Therefor there should be only one main DefaultProcessCommands. + */ + public static DefaultProcessCommands main(File directory, int processNumber) { + return new DefaultProcessCommands(directory, processNumber, true); + } + + /** + * Secondary DefaultProcessCommands will read and write to the shared memory space but will not clear it. Therefor, there + * can be any number of them. + */ + public static DefaultProcessCommands secondary(File directory, int processNumber) { + return new DefaultProcessCommands(directory, processNumber, false); + } + + /** + * Clears the shared memory space of the specified process number. + */ + public static void reset(File directory, int processNumber) { + try (DefaultProcessCommands processCommands = new DefaultProcessCommands(directory, processNumber, true)) { + // nothing else to do than open file and reset the space of specified process + } + } + + @Override + public boolean isUp() { + return delegate.isUp(); + } + + @Override + public void setUp() { + delegate.setUp(); + } + + @Override + public boolean isOperational() { + return delegate.isOperational(); + } + + @Override + public void setOperational() { + delegate.setOperational(); + } + + @Override + public void ping() { + delegate.ping(); + } + + @Override + public long getLastPing() { + return delegate.getLastPing(); + } + + @Override + public void setHttpUrl(String s) { + delegate.setHttpUrl(s); + } + + @Override + public String getHttpUrl() { + return delegate.getHttpUrl(); + } + + @Override + public void askForStop() { + delegate.askForStop(); + } + + @Override + public boolean askedForStop() { + return delegate.askedForStop(); + } + + @Override + public void askForRestart() { + delegate.askForRestart(); + } + + @Override + public boolean askedForRestart() { + return delegate.askedForRestart(); + } + + @Override + public void acknowledgeAskForRestart() { + delegate.acknowledgeAskForRestart(); + } + + @Override + public void endWatch() { + try { + close(); + } catch (Exception e) { + LoggerFactory.getLogger(getClass()).error("Failed to close DefaultProcessCommands", e); + } + } + + @Override + public void close() { + allProcessesCommands.close(); + } +} diff --git a/server/sonar-process/src/main/java/org/sonar/process/sharedmemoryfile/ProcessCommands.java b/server/sonar-process/src/main/java/org/sonar/process/sharedmemoryfile/ProcessCommands.java new file mode 100644 index 00000000000..6d4bda331ab --- /dev/null +++ b/server/sonar-process/src/main/java/org/sonar/process/sharedmemoryfile/ProcessCommands.java @@ -0,0 +1,85 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * 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 02110-1301, USA. + */ +package org.sonar.process.sharedmemoryfile; + +import java.io.File; + +/** + * Process inter-communication to : + * + * + * @see DefaultProcessCommands#main(File, int) + * @see DefaultProcessCommands#secondary(File, int) + */ +public interface ProcessCommands extends AutoCloseable { + + int MAX_PROCESSES = 5; + + boolean isUp(); + + /** + * To be executed by child process to declare that it is done starting + */ + void setUp(); + + boolean isOperational(); + + /** + * To be executed by child process to declare that it is done starting and fully operational. + * + * @throws IllegalStateException if {@link #setUp()} has not been called + */ + void setOperational(); + + void ping(); + + long getLastPing(); + + void setHttpUrl(String s); + + String getHttpUrl(); + + /** + * To be executed by monitor process to ask for child process termination + */ + void askForStop(); + + boolean askedForStop(); + + /** + * To be executed by child process to ask for restart of all child processes + */ + void askForRestart(); + + /** + * Can be called by child or monitor process to know whether child process asked for restart + */ + boolean askedForRestart(); + + /** + * To be executed by monitor process to acknowledge restart request from child process. + */ + void acknowledgeAskForRestart(); + + void endWatch(); +} diff --git a/server/sonar-process/src/main/java/org/sonar/process/sharedmemoryfile/package-info.java b/server/sonar-process/src/main/java/org/sonar/process/sharedmemoryfile/package-info.java new file mode 100644 index 00000000000..126be7e9c58 --- /dev/null +++ b/server/sonar-process/src/main/java/org/sonar/process/sharedmemoryfile/package-info.java @@ -0,0 +1,23 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * 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 02110-1301, USA. + */ +@ParametersAreNonnullByDefault +package org.sonar.process.sharedmemoryfile; + +import javax.annotation.ParametersAreNonnullByDefault; 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 deleted file mode 100644 index 4667f055fbc..00000000000 --- a/server/sonar-process/src/test/java/org/sonar/process/AllProcessesCommandsTest.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * 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 02110-1301, USA. - */ -package org.sonar.process; - -import java.io.File; -import java.io.IOException; -import org.apache.commons.io.FileUtils; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.rules.TemporaryFolder; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.fail; -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 OPERATIONAL = (byte) 0x59; - private static final byte EMPTY = (byte) 0x00; - - @Rule - public TemporaryFolder temp = new TemporaryFolder(); - @Rule - public ExpectedException expectedException = ExpectedException.none(); - - @Test - public void fail_to_init_if_dir_does_not_exist() throws Exception { - File dir = temp.newFolder(); - FileUtils.deleteQuietly(dir); - - try { - new AllProcessesCommands(dir); - fail(); - } catch (IllegalArgumentException e) { - assertThat(e).hasMessage("Not a valid directory: " + dir.getAbsolutePath()); - } - } - - @Test - public void write_and_read_up() throws IOException { - try (AllProcessesCommands commands = new AllProcessesCommands(temp.newFolder())) { - int offset = 0; - - assertThat(commands.isUp(PROCESS_NUMBER)).isFalse(); - assertThat(readByte(commands, offset)).isEqualTo(EMPTY); - - commands.setUp(PROCESS_NUMBER); - assertThat(commands.isUp(PROCESS_NUMBER)).isTrue(); - assertThat(readByte(commands, offset)).isEqualTo(UP); - } - } - - @Test - public void write_and_read_operational() throws IOException { - try (AllProcessesCommands commands = new AllProcessesCommands(temp.newFolder())) { - int offset = 3; - - assertThat(commands.isOperational(PROCESS_NUMBER)).isFalse(); - assertThat(readByte(commands, offset)).isEqualTo(EMPTY); - - commands.setOperational(PROCESS_NUMBER); - assertThat(commands.isOperational(PROCESS_NUMBER)).isTrue(); - assertThat(readByte(commands, offset)).isEqualTo(OPERATIONAL); - } - } - - @Test - public void write_and_read_ping() throws IOException { - try (AllProcessesCommands commands = new AllProcessesCommands(temp.newFolder())) { - - int offset = 4; - assertThat(readLong(commands, offset)).isEqualTo(0L); - - long currentTime = System.currentTimeMillis(); - commands.ping(PROCESS_NUMBER); - assertThat(readLong(commands, offset)).isGreaterThanOrEqualTo(currentTime); - } - } - - @Test - public void write_and_read_jmx_url() throws IOException { - try (AllProcessesCommands commands = new AllProcessesCommands(temp.newFolder())) { - - int offset = 12; - for (int i = 0; i < 500; i++) { - assertThat(readByte(commands, offset + i)).isEqualTo(EMPTY); - } - - commands.setSystemInfoUrl(PROCESS_NUMBER, "jmx:foo"); - assertThat(readByte(commands, offset)).isNotEqualTo(EMPTY); - assertThat(commands.getSystemInfoUrl(PROCESS_NUMBER)).isEqualTo("jmx:foo"); - } - } - - @Test - public void ask_for_stop() throws Exception { - try (AllProcessesCommands commands = new AllProcessesCommands(temp.newFolder())) { - int offset = 1; - - assertThat(readByte(commands, offset)).isNotEqualTo(STOP); - assertThat(commands.askedForStop(PROCESS_NUMBER)).isFalse(); - - commands.askForStop(PROCESS_NUMBER); - assertThat(commands.askedForStop(PROCESS_NUMBER)).isTrue(); - assertThat(readByte(commands, offset)).isEqualTo(STOP); - } - } - - @Test - public void ask_for_restart() throws Exception { - try (AllProcessesCommands commands = new AllProcessesCommands(temp.newFolder())) { - int offset = 2; - - assertThat(readByte(commands, offset)).isNotEqualTo(RESTART); - assertThat(commands.askedForRestart(PROCESS_NUMBER)).isFalse(); - - commands.askForRestart(PROCESS_NUMBER); - assertThat(commands.askedForRestart(PROCESS_NUMBER)).isTrue(); - assertThat(readByte(commands, offset)).isEqualTo(RESTART); - } - } - - @Test - public void acknowledgeAskForRestart_has_no_effect_when_no_restart_asked() throws Exception { - try (AllProcessesCommands commands = new AllProcessesCommands(temp.newFolder())) { - int offset = 2; - - assertThat(readByte(commands, offset)).isNotEqualTo(RESTART); - assertThat(commands.askedForRestart(PROCESS_NUMBER)).isFalse(); - - commands.acknowledgeAskForRestart(PROCESS_NUMBER); - assertThat(readByte(commands, offset)).isNotEqualTo(RESTART); - assertThat(commands.askedForRestart(PROCESS_NUMBER)).isFalse(); - } - } - - @Test - public void acknowledgeAskForRestart_resets_askForRestart_has_no_effect_when_no_restart_asked() throws Exception { - try (AllProcessesCommands commands = new AllProcessesCommands(temp.newFolder())) { - int offset = 2; - - commands.askForRestart(PROCESS_NUMBER); - assertThat(commands.askedForRestart(PROCESS_NUMBER)).isTrue(); - assertThat(readByte(commands, offset)).isEqualTo(RESTART); - - commands.acknowledgeAskForRestart(PROCESS_NUMBER); - assertThat(readByte(commands, offset)).isNotEqualTo(RESTART); - assertThat(commands.askedForRestart(PROCESS_NUMBER)).isFalse(); - } - } - - @Test - public void getProcessCommands_fails_if_processNumber_is_less_than_0() throws Exception { - try (AllProcessesCommands commands = new AllProcessesCommands(temp.newFolder())) { - int processNumber = -2; - - expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("Process number " + processNumber + " is not valid"); - - commands.createAfterClean(processNumber); - } - } - - @Test - public void getProcessCommands_fails_if_processNumber_is_higher_than_MAX_PROCESSES() throws Exception { - try (AllProcessesCommands commands = new AllProcessesCommands(temp.newFolder())) { - int processNumber = MAX_PROCESSES + 1; - - expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("Process number " + processNumber + " is not valid"); - - commands.createAfterClean(processNumber); - } - } - - @Test - public void clean_cleans_sharedMemory_of_any_process_less_than_MAX_PROCESSES() throws IOException { - try (AllProcessesCommands commands = new AllProcessesCommands(temp.newFolder())) { - for (int i = 0; i < MAX_PROCESSES; i++) { - commands.create(i).setUp(); - } - commands.clean(); - for (int i = 0; i < MAX_PROCESSES; i++) { - assertThat(commands.create(i).isUp()).isFalse(); - } - } - } - - private byte readByte(AllProcessesCommands commands, int offset) { - return commands.mappedByteBuffer.get(commands.offset(PROCESS_NUMBER) + offset); - } - - private long readLong(AllProcessesCommands commands, int offset) { - return commands.mappedByteBuffer.getLong(offset + commands.offset(PROCESS_NUMBER)); - } -} 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 deleted file mode 100644 index 3c311c208de..00000000000 --- a/server/sonar-process/src/test/java/org/sonar/process/DefaultProcessCommandsTest.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * 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 02110-1301, USA. - */ -package org.sonar.process; - -import java.io.File; -import java.io.IOException; -import org.apache.commons.io.FileUtils; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.rules.TemporaryFolder; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.fail; -import static org.sonar.process.ProcessCommands.MAX_PROCESSES; - -public class DefaultProcessCommandsTest { - - private static final int PROCESS_NUMBER = 1; - - @Rule - public TemporaryFolder temp = new TemporaryFolder(); - @Rule - public ExpectedException expectedException = ExpectedException.none(); - - @Test - public void fail_to_init_if_dir_does_not_exist() throws Exception { - File dir = temp.newFolder(); - FileUtils.deleteQuietly(dir); - - try { - DefaultProcessCommands.main(dir, PROCESS_NUMBER); - fail(); - } catch (IllegalArgumentException e) { - assertThat(e).hasMessage("Not a valid directory: " + dir.getAbsolutePath()); - } - } - - @Test - public void main_clears_the_memory_space_of_the_specified_process_number() throws IOException { - File dir = temp.newFolder(); - - try (DefaultProcessCommands commands = DefaultProcessCommands.main(dir, PROCESS_NUMBER)) { - commands.setUp(); - commands.setHttpUrl("bla"); - commands.setOperational(); - } - - try (DefaultProcessCommands commands = DefaultProcessCommands.main(dir, PROCESS_NUMBER)) { - assertThat(commands.isUp()).isFalse(); - assertThat(commands.getHttpUrl()).isEmpty(); - assertThat(commands.isOperational()).isFalse(); - } - } - - @Test - public void secondary_does_not_clear_the_memory_space_of_the_specified_process_number() throws IOException { - File dir = temp.newFolder(); - - try (DefaultProcessCommands commands = DefaultProcessCommands.main(dir, PROCESS_NUMBER)) { - commands.setUp(); - commands.setHttpUrl("bla"); - commands.setOperational(); - } - - try (DefaultProcessCommands commands = DefaultProcessCommands.secondary(dir, PROCESS_NUMBER)) { - assertThat(commands.isUp()).isTrue(); - assertThat(commands.getHttpUrl()).isEqualTo("bla"); - assertThat(commands.isOperational()).isTrue(); - } - } - - @Test - public void child_process_update_the_mapped_memory() throws Exception { - File dir = temp.newFolder(); - - try (DefaultProcessCommands commands = DefaultProcessCommands.main(dir, PROCESS_NUMBER)) { - assertThat(commands.isUp()).isFalse(); - commands.setUp(); - assertThat(commands.isUp()).isTrue(); - } - } - - @Test - public void reset_clears_only_the_memory_space_of_specified_process_number() throws IOException { - File dir = temp.newFolder(); - - try (AllProcessesCommands commands = new AllProcessesCommands(dir)) { - for (int i = 0; i < MAX_PROCESSES; i++) { - commands.setOperational(i); - commands.setUp(i); - } - - int resetProcess = 3; - DefaultProcessCommands.reset(dir, resetProcess); - for (int i = 0; i < MAX_PROCESSES; i++) { - assertThat(commands.isOperational(i)).isEqualTo(i != resetProcess); - assertThat(commands.isUp(i)).isEqualTo(i != resetProcess); - } - } - } - - @Test - public void ask_for_stop() throws Exception { - File dir = temp.newFolder(); - - try (DefaultProcessCommands commands = DefaultProcessCommands.main(dir, PROCESS_NUMBER)) { - assertThat(commands.askedForStop()).isFalse(); - - commands.askForStop(); - assertThat(commands.askedForStop()).isTrue(); - } - } - - @Test - public void ask_for_restart() throws Exception { - File dir = temp.newFolder(); - - try (DefaultProcessCommands commands = DefaultProcessCommands.main(dir, PROCESS_NUMBER)) { - assertThat(commands.askedForRestart()).isFalse(); - - commands.askForRestart(); - assertThat(commands.askedForRestart()).isTrue(); - } - } - - @Test - public void acknowledgeAskForRestart_has_no_effect_when_no_restart_asked() throws Exception { - File dir = temp.newFolder(); - - try (DefaultProcessCommands commands = DefaultProcessCommands.main(dir, PROCESS_NUMBER)) { - assertThat(commands.askedForRestart()).isFalse(); - - commands.acknowledgeAskForRestart(); - assertThat(commands.askedForRestart()).isFalse(); - } - } - - @Test - public void acknowledgeAskForRestart_resets_askForRestart_has_no_effect_when_no_restart_asked() throws Exception { - File dir = temp.newFolder(); - - try (DefaultProcessCommands commands = DefaultProcessCommands.main(dir, PROCESS_NUMBER)) { - commands.askForRestart(); - assertThat(commands.askedForRestart()).isTrue(); - - commands.acknowledgeAskForRestart(); - assertThat(commands.askedForRestart()).isFalse(); - } - } - - @Test - public void main_fails_if_processNumber_is_less_than_0() throws Exception { - int processNumber = -2; - - expectProcessNumberNoValidIAE(processNumber); - - - try (DefaultProcessCommands main = DefaultProcessCommands.main(temp.newFolder(), processNumber)) { - - } - } - - @Test - public void main_fails_if_processNumber_is_higher_than_MAX_PROCESSES() throws Exception { - int processNumber = MAX_PROCESSES + 1; - - expectProcessNumberNoValidIAE(processNumber); - - try (DefaultProcessCommands main = DefaultProcessCommands.main(temp.newFolder(), processNumber)) { - - } - } - - @Test - public void main_fails_if_processNumber_is_MAX_PROCESSES() throws Exception { - int processNumber = MAX_PROCESSES; - - expectProcessNumberNoValidIAE(processNumber); - - try (DefaultProcessCommands main = DefaultProcessCommands.main(temp.newFolder(), processNumber)) { - - } - } - - @Test - public void secondary_fails_if_processNumber_is_less_than_0() throws Exception { - int processNumber = -2; - - expectProcessNumberNoValidIAE(processNumber); - - DefaultProcessCommands.secondary(temp.newFolder(), processNumber); - } - - @Test - public void secondary_fails_if_processNumber_is_higher_than_MAX_PROCESSES() throws Exception { - int processNumber = MAX_PROCESSES + 1; - - expectProcessNumberNoValidIAE(processNumber); - - try (DefaultProcessCommands secondary = DefaultProcessCommands.secondary(temp.newFolder(), processNumber)) { - - } - } - - @Test - public void reset_fails_if_processNumber_is_less_than_0() throws Exception { - int processNumber = -2; - - expectProcessNumberNoValidIAE(processNumber); - - DefaultProcessCommands.reset(temp.newFolder(), processNumber); - } - - @Test - public void reset_fails_if_processNumber_is_higher_than_MAX_PROCESSES() throws Exception { - int processNumber = MAX_PROCESSES + 1; - - expectProcessNumberNoValidIAE(processNumber); - - DefaultProcessCommands.reset(temp.newFolder(), processNumber); - } - - private void expectProcessNumberNoValidIAE(int processNumber) { - expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("Process number " + processNumber + " is not valid"); - } -} 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 68996625c72..3b7a4659d31 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 @@ -28,6 +28,7 @@ import org.junit.rules.TemporaryFolder; import org.junit.rules.TestRule; import org.junit.rules.Timeout; import org.sonar.process.Lifecycle.State; +import org.sonar.process.sharedmemoryfile.ProcessCommands; import org.sonar.process.test.StandardProcess; import java.io.File; diff --git a/server/sonar-process/src/test/java/org/sonar/process/StopWatcherTest.java b/server/sonar-process/src/test/java/org/sonar/process/StopWatcherTest.java index 9938ae1050c..fa5704f37e9 100644 --- a/server/sonar-process/src/test/java/org/sonar/process/StopWatcherTest.java +++ b/server/sonar-process/src/test/java/org/sonar/process/StopWatcherTest.java @@ -24,6 +24,7 @@ import org.junit.Test; import org.junit.rules.DisableOnDebug; import org.junit.rules.TestRule; import org.junit.rules.Timeout; +import org.sonar.process.sharedmemoryfile.ProcessCommands; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; diff --git a/server/sonar-process/src/test/java/org/sonar/process/StopperThreadTest.java b/server/sonar-process/src/test/java/org/sonar/process/StopperThreadTest.java index cfd00bf01c8..e62c479e1bf 100644 --- a/server/sonar-process/src/test/java/org/sonar/process/StopperThreadTest.java +++ b/server/sonar-process/src/test/java/org/sonar/process/StopperThreadTest.java @@ -22,6 +22,7 @@ package org.sonar.process; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import org.sonar.process.sharedmemoryfile.ProcessCommands; import static org.mockito.Mockito.*; diff --git a/server/sonar-process/src/test/java/org/sonar/process/sharedmemoryfile/AllProcessesCommandsTest.java b/server/sonar-process/src/test/java/org/sonar/process/sharedmemoryfile/AllProcessesCommandsTest.java new file mode 100644 index 00000000000..2bad735aaa6 --- /dev/null +++ b/server/sonar-process/src/test/java/org/sonar/process/sharedmemoryfile/AllProcessesCommandsTest.java @@ -0,0 +1,218 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * 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 02110-1301, USA. + */ +package org.sonar.process.sharedmemoryfile; + +import java.io.File; +import java.io.IOException; +import org.apache.commons.io.FileUtils; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.fail; +import static org.sonar.process.sharedmemoryfile.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 OPERATIONAL = (byte) 0x59; + private static final byte EMPTY = (byte) 0x00; + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void fail_to_init_if_dir_does_not_exist() throws Exception { + File dir = temp.newFolder(); + FileUtils.deleteQuietly(dir); + + try { + new AllProcessesCommands(dir); + fail(); + } catch (IllegalArgumentException e) { + assertThat(e).hasMessage("Not a valid directory: " + dir.getAbsolutePath()); + } + } + + @Test + public void write_and_read_up() throws IOException { + try (AllProcessesCommands commands = new AllProcessesCommands(temp.newFolder())) { + int offset = 0; + + assertThat(commands.isUp(PROCESS_NUMBER)).isFalse(); + assertThat(readByte(commands, offset)).isEqualTo(EMPTY); + + commands.setUp(PROCESS_NUMBER); + assertThat(commands.isUp(PROCESS_NUMBER)).isTrue(); + assertThat(readByte(commands, offset)).isEqualTo(UP); + } + } + + @Test + public void write_and_read_operational() throws IOException { + try (AllProcessesCommands commands = new AllProcessesCommands(temp.newFolder())) { + int offset = 3; + + assertThat(commands.isOperational(PROCESS_NUMBER)).isFalse(); + assertThat(readByte(commands, offset)).isEqualTo(EMPTY); + + commands.setOperational(PROCESS_NUMBER); + assertThat(commands.isOperational(PROCESS_NUMBER)).isTrue(); + assertThat(readByte(commands, offset)).isEqualTo(OPERATIONAL); + } + } + + @Test + public void write_and_read_ping() throws IOException { + try (AllProcessesCommands commands = new AllProcessesCommands(temp.newFolder())) { + + int offset = 4; + assertThat(readLong(commands, offset)).isEqualTo(0L); + + long currentTime = System.currentTimeMillis(); + commands.ping(PROCESS_NUMBER); + assertThat(readLong(commands, offset)).isGreaterThanOrEqualTo(currentTime); + } + } + + @Test + public void write_and_read_jmx_url() throws IOException { + try (AllProcessesCommands commands = new AllProcessesCommands(temp.newFolder())) { + + int offset = 12; + for (int i = 0; i < 500; i++) { + assertThat(readByte(commands, offset + i)).isEqualTo(EMPTY); + } + + commands.setSystemInfoUrl(PROCESS_NUMBER, "jmx:foo"); + assertThat(readByte(commands, offset)).isNotEqualTo(EMPTY); + assertThat(commands.getSystemInfoUrl(PROCESS_NUMBER)).isEqualTo("jmx:foo"); + } + } + + @Test + public void ask_for_stop() throws Exception { + try (AllProcessesCommands commands = new AllProcessesCommands(temp.newFolder())) { + int offset = 1; + + assertThat(readByte(commands, offset)).isNotEqualTo(STOP); + assertThat(commands.askedForStop(PROCESS_NUMBER)).isFalse(); + + commands.askForStop(PROCESS_NUMBER); + assertThat(commands.askedForStop(PROCESS_NUMBER)).isTrue(); + assertThat(readByte(commands, offset)).isEqualTo(STOP); + } + } + + @Test + public void ask_for_restart() throws Exception { + try (AllProcessesCommands commands = new AllProcessesCommands(temp.newFolder())) { + int offset = 2; + + assertThat(readByte(commands, offset)).isNotEqualTo(RESTART); + assertThat(commands.askedForRestart(PROCESS_NUMBER)).isFalse(); + + commands.askForRestart(PROCESS_NUMBER); + assertThat(commands.askedForRestart(PROCESS_NUMBER)).isTrue(); + assertThat(readByte(commands, offset)).isEqualTo(RESTART); + } + } + + @Test + public void acknowledgeAskForRestart_has_no_effect_when_no_restart_asked() throws Exception { + try (AllProcessesCommands commands = new AllProcessesCommands(temp.newFolder())) { + int offset = 2; + + assertThat(readByte(commands, offset)).isNotEqualTo(RESTART); + assertThat(commands.askedForRestart(PROCESS_NUMBER)).isFalse(); + + commands.acknowledgeAskForRestart(PROCESS_NUMBER); + assertThat(readByte(commands, offset)).isNotEqualTo(RESTART); + assertThat(commands.askedForRestart(PROCESS_NUMBER)).isFalse(); + } + } + + @Test + public void acknowledgeAskForRestart_resets_askForRestart_has_no_effect_when_no_restart_asked() throws Exception { + try (AllProcessesCommands commands = new AllProcessesCommands(temp.newFolder())) { + int offset = 2; + + commands.askForRestart(PROCESS_NUMBER); + assertThat(commands.askedForRestart(PROCESS_NUMBER)).isTrue(); + assertThat(readByte(commands, offset)).isEqualTo(RESTART); + + commands.acknowledgeAskForRestart(PROCESS_NUMBER); + assertThat(readByte(commands, offset)).isNotEqualTo(RESTART); + assertThat(commands.askedForRestart(PROCESS_NUMBER)).isFalse(); + } + } + + @Test + public void getProcessCommands_fails_if_processNumber_is_less_than_0() throws Exception { + try (AllProcessesCommands commands = new AllProcessesCommands(temp.newFolder())) { + int processNumber = -2; + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Process number " + processNumber + " is not valid"); + + commands.createAfterClean(processNumber); + } + } + + @Test + public void getProcessCommands_fails_if_processNumber_is_higher_than_MAX_PROCESSES() throws Exception { + try (AllProcessesCommands commands = new AllProcessesCommands(temp.newFolder())) { + int processNumber = MAX_PROCESSES + 1; + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Process number " + processNumber + " is not valid"); + + commands.createAfterClean(processNumber); + } + } + + @Test + public void clean_cleans_sharedMemory_of_any_process_less_than_MAX_PROCESSES() throws IOException { + try (AllProcessesCommands commands = new AllProcessesCommands(temp.newFolder())) { + for (int i = 0; i < MAX_PROCESSES; i++) { + commands.create(i).setUp(); + } + commands.clean(); + for (int i = 0; i < MAX_PROCESSES; i++) { + assertThat(commands.create(i).isUp()).isFalse(); + } + } + } + + private byte readByte(AllProcessesCommands commands, int offset) { + return commands.mappedByteBuffer.get(commands.offset(PROCESS_NUMBER) + offset); + } + + private long readLong(AllProcessesCommands commands, int offset) { + return commands.mappedByteBuffer.getLong(offset + commands.offset(PROCESS_NUMBER)); + } +} diff --git a/server/sonar-process/src/test/java/org/sonar/process/sharedmemoryfile/DefaultProcessCommandsTest.java b/server/sonar-process/src/test/java/org/sonar/process/sharedmemoryfile/DefaultProcessCommandsTest.java new file mode 100644 index 00000000000..76d450e3bc8 --- /dev/null +++ b/server/sonar-process/src/test/java/org/sonar/process/sharedmemoryfile/DefaultProcessCommandsTest.java @@ -0,0 +1,245 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * 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 02110-1301, USA. + */ +package org.sonar.process.sharedmemoryfile; + +import java.io.File; +import java.io.IOException; +import org.apache.commons.io.FileUtils; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.fail; +import static org.sonar.process.sharedmemoryfile.ProcessCommands.MAX_PROCESSES; + +public class DefaultProcessCommandsTest { + + private static final int PROCESS_NUMBER = 1; + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void fail_to_init_if_dir_does_not_exist() throws Exception { + File dir = temp.newFolder(); + FileUtils.deleteQuietly(dir); + + try { + DefaultProcessCommands.main(dir, PROCESS_NUMBER); + fail(); + } catch (IllegalArgumentException e) { + assertThat(e).hasMessage("Not a valid directory: " + dir.getAbsolutePath()); + } + } + + @Test + public void main_clears_the_memory_space_of_the_specified_process_number() throws IOException { + File dir = temp.newFolder(); + + try (DefaultProcessCommands commands = DefaultProcessCommands.main(dir, PROCESS_NUMBER)) { + commands.setUp(); + commands.setHttpUrl("bla"); + commands.setOperational(); + } + + try (DefaultProcessCommands commands = DefaultProcessCommands.main(dir, PROCESS_NUMBER)) { + assertThat(commands.isUp()).isFalse(); + assertThat(commands.getHttpUrl()).isEmpty(); + assertThat(commands.isOperational()).isFalse(); + } + } + + @Test + public void secondary_does_not_clear_the_memory_space_of_the_specified_process_number() throws IOException { + File dir = temp.newFolder(); + + try (DefaultProcessCommands commands = DefaultProcessCommands.main(dir, PROCESS_NUMBER)) { + commands.setUp(); + commands.setHttpUrl("bla"); + commands.setOperational(); + } + + try (DefaultProcessCommands commands = DefaultProcessCommands.secondary(dir, PROCESS_NUMBER)) { + assertThat(commands.isUp()).isTrue(); + assertThat(commands.getHttpUrl()).isEqualTo("bla"); + assertThat(commands.isOperational()).isTrue(); + } + } + + @Test + public void child_process_update_the_mapped_memory() throws Exception { + File dir = temp.newFolder(); + + try (DefaultProcessCommands commands = DefaultProcessCommands.main(dir, PROCESS_NUMBER)) { + assertThat(commands.isUp()).isFalse(); + commands.setUp(); + assertThat(commands.isUp()).isTrue(); + } + } + + @Test + public void reset_clears_only_the_memory_space_of_specified_process_number() throws IOException { + File dir = temp.newFolder(); + + try (AllProcessesCommands commands = new AllProcessesCommands(dir)) { + for (int i = 0; i < MAX_PROCESSES; i++) { + commands.setOperational(i); + commands.setUp(i); + } + + int resetProcess = 3; + DefaultProcessCommands.reset(dir, resetProcess); + for (int i = 0; i < MAX_PROCESSES; i++) { + assertThat(commands.isOperational(i)).isEqualTo(i != resetProcess); + assertThat(commands.isUp(i)).isEqualTo(i != resetProcess); + } + } + } + + @Test + public void ask_for_stop() throws Exception { + File dir = temp.newFolder(); + + try (DefaultProcessCommands commands = DefaultProcessCommands.main(dir, PROCESS_NUMBER)) { + assertThat(commands.askedForStop()).isFalse(); + + commands.askForStop(); + assertThat(commands.askedForStop()).isTrue(); + } + } + + @Test + public void ask_for_restart() throws Exception { + File dir = temp.newFolder(); + + try (DefaultProcessCommands commands = DefaultProcessCommands.main(dir, PROCESS_NUMBER)) { + assertThat(commands.askedForRestart()).isFalse(); + + commands.askForRestart(); + assertThat(commands.askedForRestart()).isTrue(); + } + } + + @Test + public void acknowledgeAskForRestart_has_no_effect_when_no_restart_asked() throws Exception { + File dir = temp.newFolder(); + + try (DefaultProcessCommands commands = DefaultProcessCommands.main(dir, PROCESS_NUMBER)) { + assertThat(commands.askedForRestart()).isFalse(); + + commands.acknowledgeAskForRestart(); + assertThat(commands.askedForRestart()).isFalse(); + } + } + + @Test + public void acknowledgeAskForRestart_resets_askForRestart_has_no_effect_when_no_restart_asked() throws Exception { + File dir = temp.newFolder(); + + try (DefaultProcessCommands commands = DefaultProcessCommands.main(dir, PROCESS_NUMBER)) { + commands.askForRestart(); + assertThat(commands.askedForRestart()).isTrue(); + + commands.acknowledgeAskForRestart(); + assertThat(commands.askedForRestart()).isFalse(); + } + } + + @Test + public void main_fails_if_processNumber_is_less_than_0() throws Exception { + int processNumber = -2; + + expectProcessNumberNoValidIAE(processNumber); + + + try (DefaultProcessCommands main = DefaultProcessCommands.main(temp.newFolder(), processNumber)) { + + } + } + + @Test + public void main_fails_if_processNumber_is_higher_than_MAX_PROCESSES() throws Exception { + int processNumber = MAX_PROCESSES + 1; + + expectProcessNumberNoValidIAE(processNumber); + + try (DefaultProcessCommands main = DefaultProcessCommands.main(temp.newFolder(), processNumber)) { + + } + } + + @Test + public void main_fails_if_processNumber_is_MAX_PROCESSES() throws Exception { + int processNumber = MAX_PROCESSES; + + expectProcessNumberNoValidIAE(processNumber); + + try (DefaultProcessCommands main = DefaultProcessCommands.main(temp.newFolder(), processNumber)) { + + } + } + + @Test + public void secondary_fails_if_processNumber_is_less_than_0() throws Exception { + int processNumber = -2; + + expectProcessNumberNoValidIAE(processNumber); + + DefaultProcessCommands.secondary(temp.newFolder(), processNumber); + } + + @Test + public void secondary_fails_if_processNumber_is_higher_than_MAX_PROCESSES() throws Exception { + int processNumber = MAX_PROCESSES + 1; + + expectProcessNumberNoValidIAE(processNumber); + + try (DefaultProcessCommands secondary = DefaultProcessCommands.secondary(temp.newFolder(), processNumber)) { + + } + } + + @Test + public void reset_fails_if_processNumber_is_less_than_0() throws Exception { + int processNumber = -2; + + expectProcessNumberNoValidIAE(processNumber); + + DefaultProcessCommands.reset(temp.newFolder(), processNumber); + } + + @Test + public void reset_fails_if_processNumber_is_higher_than_MAX_PROCESSES() throws Exception { + int processNumber = MAX_PROCESSES + 1; + + expectProcessNumberNoValidIAE(processNumber); + + DefaultProcessCommands.reset(temp.newFolder(), processNumber); + } + + private void expectProcessNumberNoValidIAE(int processNumber) { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Process number " + processNumber + " is not valid"); + } +} 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 93110668414..8f7ab283049 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 @@ -25,7 +25,7 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.server.handler.ContextHandler; import org.sonar.process.Monitored; -import org.sonar.process.ProcessCommands; +import org.sonar.process.sharedmemoryfile.ProcessCommands; import org.sonar.process.ProcessEntryPoint; import javax.servlet.ServletException; diff --git a/server/sonar-server/src/main/java/org/sonar/ce/http/CeHttpClientImpl.java b/server/sonar-server/src/main/java/org/sonar/ce/http/CeHttpClientImpl.java index 09f0f8abeaa..e119aa7948b 100644 --- a/server/sonar-server/src/main/java/org/sonar/ce/http/CeHttpClientImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/ce/http/CeHttpClientImpl.java @@ -28,7 +28,7 @@ import okhttp3.RequestBody; import org.apache.commons.io.IOUtils; import org.sonar.api.config.Configuration; import org.sonar.api.utils.log.LoggerLevel; -import org.sonar.process.DefaultProcessCommands; +import org.sonar.process.sharedmemoryfile.DefaultProcessCommands; import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo; import static java.util.Objects.requireNonNull; diff --git a/server/sonar-server/src/main/java/org/sonar/server/app/ProcessCommandWrapperImpl.java b/server/sonar-server/src/main/java/org/sonar/server/app/ProcessCommandWrapperImpl.java index 54e998e9576..65732bd45c7 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/app/ProcessCommandWrapperImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/app/ProcessCommandWrapperImpl.java @@ -21,8 +21,8 @@ package org.sonar.server.app; import java.io.File; import org.sonar.api.config.Configuration; -import org.sonar.process.DefaultProcessCommands; -import org.sonar.process.ProcessCommands; +import org.sonar.process.sharedmemoryfile.DefaultProcessCommands; +import org.sonar.process.sharedmemoryfile.ProcessCommands; import static org.sonar.process.ProcessEntryPoint.PROPERTY_PROCESS_INDEX; import static org.sonar.process.ProcessEntryPoint.PROPERTY_SHARED_PATH; diff --git a/server/sonar-server/src/main/java/org/sonar/server/app/WebServer.java b/server/sonar-server/src/main/java/org/sonar/server/app/WebServer.java index 3b6b5d1629b..31016f4a0b2 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/app/WebServer.java +++ b/server/sonar-server/src/main/java/org/sonar/server/app/WebServer.java @@ -21,7 +21,7 @@ package org.sonar.server.app; import com.google.common.collect.ImmutableMap; import java.io.File; -import org.sonar.process.DefaultProcessCommands; +import org.sonar.process.sharedmemoryfile.DefaultProcessCommands; import org.sonar.process.MinimumViableSystem; import org.sonar.process.Monitored; import org.sonar.process.ProcessEntryPoint; diff --git a/server/sonar-server/src/test/java/org/sonar/ce/http/CeHttpClientTest.java b/server/sonar-server/src/test/java/org/sonar/ce/http/CeHttpClientTest.java index 2e121fc8ddd..2234c209618 100644 --- a/server/sonar-server/src/test/java/org/sonar/ce/http/CeHttpClientTest.java +++ b/server/sonar-server/src/test/java/org/sonar/ce/http/CeHttpClientTest.java @@ -32,7 +32,7 @@ import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.sonar.api.config.internal.MapSettings; import org.sonar.api.utils.log.LoggerLevel; -import org.sonar.process.DefaultProcessCommands; +import org.sonar.process.sharedmemoryfile.DefaultProcessCommands; import org.sonar.process.ProcessEntryPoint; import org.sonar.process.ProcessId; import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo; diff --git a/server/sonar-server/src/test/java/org/sonar/server/app/ProcessCommandWrapperImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/app/ProcessCommandWrapperImplTest.java index b34daac76eb..f328f10de44 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/app/ProcessCommandWrapperImplTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/app/ProcessCommandWrapperImplTest.java @@ -26,7 +26,7 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.sonar.api.config.internal.MapSettings; -import org.sonar.process.DefaultProcessCommands; +import org.sonar.process.sharedmemoryfile.DefaultProcessCommands; import static org.assertj.core.api.Assertions.assertThat; import static org.sonar.process.ProcessEntryPoint.PROPERTY_PROCESS_INDEX;