package co.codewizards.cloudstore.test; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.security.SecureRandom; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.atomic.AtomicInteger; import co.codewizards.cloudstore.core.config.ConfigDir; import co.codewizards.cloudstore.core.util.IOUtil; import co.codewizards.cloudstore.server.CloudStoreServer; public class CloudStoreServerTestSupport { private static final SecureRandom random = new SecureRandom(); private static final AtomicInteger cloudStoreServerStopTimerIndex = new AtomicInteger(); private CloudStoreServer cloudStoreServer; private Thread cloudStoreServerThread; private final Object cloudStoreServerMutex = new Object(); private final Timer cloudStoreServerStopTimer = new Timer("cloudStoreServerStopTimer-" + cloudStoreServerStopTimerIndex.incrementAndGet(), true); private TimerTask cloudStoreServerStopTimerTask; /** * When running tests in parallel, the beforeClass() and afterClass() seem to be invoked multiple times. */ private int testInstanceCounter; private int securePort; public int getSecurePort() { return securePort; } public String getSecureUrl() { return "https://localhost:" + getSecurePort(); } /** * @return <code>true</code>, if this is the first invocation. <code>false</code> afterwards. */ public boolean beforeClass() throws Exception { synchronized (cloudStoreServerMutex) { final boolean first = testInstanceCounter++ == 0; if (cloudStoreServerStopTimerTask != null) { cloudStoreServerStopTimerTask.cancel(); cloudStoreServerStopTimerTask = null; } if (cloudStoreServer == null) { IOUtil.deleteDirectoryRecursively(ConfigDir.getInstance().getFile()); // securePort = 1024 + 1 + random.nextInt(10240); securePort = getRandomAvailableServerPort(); cloudStoreServer = createCloudStoreServer(); cloudStoreServer.setSecurePort(securePort); cloudStoreServerThread = new Thread(cloudStoreServer); cloudStoreServerThread.setName("cloudStoreServerThread"); cloudStoreServerThread.setDaemon(true); cloudStoreServerThread.start(); waitForServerToOpenSecurePort(); } return first; } } private int getRandomAvailableServerPort() throws IOException { final ServerSocket serverSocket = new ServerSocket(0); final int port = serverSocket.getLocalPort(); serverSocket.close(); return port; } protected CloudStoreServer createCloudStoreServer() { return new CloudStoreServer(); } private void waitForServerToOpenSecurePort() { final long timeoutMillis = 3 * 60_000L; final long begin = System.currentTimeMillis(); while (true) { try { final Socket socket = new Socket("localhost", getSecurePort()); socket.close(); return; // success! } catch (final Exception x) { try { Thread.sleep(1000); } catch (final InterruptedException ie) { } } if (System.currentTimeMillis() - begin > timeoutMillis) throw new IllegalStateException("Server did not start within timeout (ms): " + timeoutMillis); } } /** * @return <code>true</code>, if this is the last invocation. <code>false</code> before. */ public boolean afterClass() throws Exception { synchronized (cloudStoreServerMutex) { if (--testInstanceCounter > 0) return false; if (cloudStoreServerStopTimerTask == null) { cloudStoreServerStopTimerTask = new TimerTask() { @Override public void run() { synchronized (cloudStoreServerMutex) { if (cloudStoreServer != null) { cloudStoreServer.stop(); cloudStoreServer = null; cloudStoreServerStopTimerTask = null; } } } }; cloudStoreServerStopTimer.schedule(cloudStoreServerStopTimerTask, 60000L); } return true; } } }