Mercurial > hg > thermostat-ng > web-gateway
view tests/test-utils/src/main/java/com/redhat/thermostat/gateway/tests/utils/MongodTestUtil.java @ 268:e159ff4b557f
tests: MongodTestUtil: throw exception on socket close timeout, add some comments
author | Zdenek Zambersky <zzambers@redhat.com> |
---|---|
date | Tue, 03 Oct 2017 14:54:00 +0200 |
parents | 2de9f7a29768 |
children | 0ec0aad214cb |
line wrap: on
line source
/* * Copyright 2012-2017 Red Hat, Inc. * * This file is part of Thermostat. * * Thermostat is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2, or (at your * option) any later version. * * Thermostat 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Thermostat; see the file COPYING. If not see * <http://www.gnu.org/licenses/>. * * Linking this code with other modules is making a combined work * based on this code. Thus, the terms and conditions of the GNU * General Public License cover the whole combination. * * As a special exception, the copyright holders of this code give * you permission to link this code with independent modules to * produce an executable, regardless of the license terms of these * independent modules, and to copy and distribute the resulting * executable under terms of your choice, provided that you also * meet, for each linked independent module, the terms and conditions * of the license of that module. An independent module is a module * which is not derived from or based on this code. If you modify * this code, you may extend this exception to your version of the * library, but you are not obligated to do so. If you do not wish * to do so, delete this exception statement from your version. */ package com.redhat.thermostat.gateway.tests.utils; import java.io.IOException; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import org.bson.Document; import com.mongodb.MongoClient; import com.mongodb.ServerAddress; import com.mongodb.client.MongoCollection; import com.redhat.thermostat.gateway.common.util.OS; import java.net.ServerSocket; import java.net.SocketAddress; import java.net.InetSocketAddress; public class MongodTestUtil { private static final int WAIT_FOR_MAX_ITERATIONS = 100; private static final long WAIT_FOR_SLEEP_DURATION = 100L; private static final int DEFAULT_PORT = 27519; private final String databaseName = "thermostat"; private final String host = "127.0.0.1"; private final int port; public final String listenAddress; private MongoClient mongoClient; private Path tempDbDir; private Path tempLogFile; public Process process; private boolean connectedToDatabase; public MongodTestUtil() { this(DEFAULT_PORT); } public MongodTestUtil(int port) { this.port = port; this.listenAddress = host + ":" + port; } public void startMongod() throws IOException, InterruptedException { tempDbDir = Files.createTempDirectory("tms-mongo"); tempDbDir.toFile().deleteOnExit(); Files.createDirectories(tempDbDir.resolve("data/db")); tempLogFile = tempDbDir.resolve("mongod.log"); tempLogFile.toFile().deleteOnExit(); String[] posixCommand = {"mongod", "--dbpath", tempDbDir.resolve("data/db").toAbsolutePath().toString(), "--port", String.valueOf(port), "--fork", "--logpath", tempLogFile.toAbsolutePath().toString()}; String[] windowsCommand = {"cmd", "/c", "mongod", "--dbpath", tempDbDir.resolve("data/db").toAbsolutePath().toString(), "--port", String.valueOf(port), "--logpath", tempLogFile.toAbsolutePath().toString()}; ProcessBuilder builder = new ProcessBuilder(OS.IS_UNIX ? posixCommand : windowsCommand); process = builder.start(); mongoClient = new MongoClient(new ServerAddress(host, port)); connectedToDatabase = waitForMongodStart(); } public void stopMongod() throws IOException, InterruptedException { if (connectedToDatabase) { try { mongoClient.getDatabase("admin").runCommand(new Document("shutdown", 1)); } catch (Exception ignored) { /* following exception is always thrown: com.mongodb.MongoSocketReadException: Prematurely reached end of stream ( probably expected, connection get closed? ) */ } mongoClient.close(); mongoClient = null; waitForMongodStop(); waitForSocketToClose(port); finish(); } } public void dropCollection(String collectionName) { mongoClient.getDatabase(databaseName).getCollection(collectionName).drop(); } public MongoCollection<Document> getCollection(String collectionName) { return mongoClient.getDatabase(databaseName).getCollection(collectionName); } private void finish() throws IOException { Files.walkFileTree(tempDbDir, new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { Files.delete(file); return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { Files.delete(dir); return FileVisitResult.CONTINUE; } }); } private boolean waitForMongodStart() throws IOException, InterruptedException { return waitFor("waiting for connections on port", "addr already in use"); } private boolean waitForMongodStop() throws IOException, InterruptedException { // dbexit string is not logged by newer mongodb (>= 3.4 ?) return waitFor("dbexit: rc: 0"); } private boolean waitFor(String match) throws IOException, InterruptedException { return waitFor(match, ""); } /** * Keeps checking the temporary log file to see if any of the desired * matches are found in the file. This allows short-circuiting of checking * the files in the event some error happens (like being unable to connect * to the database) so it does not keep cycling for a long period of time. * @param desiredMatch The string to find, returns true if found. Null * should not be passed to this argument. * @param errorMatch An error string to return false on finding. An empty * string should be used if no error match is needed (it * is better to call waitFor(desiredMatch) instead). Null * should not be used. * @return True if any of the desired matches were found, false if an error * match was found or it timed out. */ private boolean waitFor(String desiredMatch, String errorMatch) throws IOException, InterruptedException { for (int i = 0; i < WAIT_FOR_MAX_ITERATIONS; i++) { if (Files.exists(tempLogFile)) { String logFileText = new String(Files.readAllBytes(tempLogFile)); if (logFileText.contains(desiredMatch)) { return true; } if (!"".equals(errorMatch) && logFileText.contains(errorMatch)) { return false; } } Thread.sleep(WAIT_FOR_SLEEP_DURATION); } return false; } private void waitForSocketToClose(int port) throws InterruptedException { for (int i = 0; i < WAIT_FOR_MAX_ITERATIONS; ++i) { /* Try to bind socket, if it fails, port is still in use */ try (ServerSocket socket = new ServerSocket()) { SocketAddress address = new InetSocketAddress(port); socket.bind(address, 0); return; } catch (IOException e) { /* ignored */ } Thread.sleep(WAIT_FOR_SLEEP_DURATION); } throw new RuntimeException("Timeout waiting for mongodb socket to close reached!"); } public boolean isConnectedToDatabase() { return connectedToDatabase; } }