001package co.codewizards.cloudstore.test; 002 003import java.io.IOException; 004import java.net.ServerSocket; 005import java.net.Socket; 006import java.security.SecureRandom; 007import java.util.Timer; 008import java.util.TimerTask; 009import java.util.concurrent.atomic.AtomicInteger; 010 011import co.codewizards.cloudstore.core.config.ConfigDir; 012import co.codewizards.cloudstore.core.util.IOUtil; 013import co.codewizards.cloudstore.server.CloudStoreServer; 014 015public class CloudStoreServerTestSupport { 016 017 private static final SecureRandom random = new SecureRandom(); 018 private static final AtomicInteger cloudStoreServerStopTimerIndex = new AtomicInteger(); 019 private CloudStoreServer cloudStoreServer; 020 private Thread cloudStoreServerThread; 021 private final Object cloudStoreServerMutex = new Object(); 022 private final Timer cloudStoreServerStopTimer = new Timer("cloudStoreServerStopTimer-" + cloudStoreServerStopTimerIndex.incrementAndGet(), true); 023 private TimerTask cloudStoreServerStopTimerTask; 024 025 /** 026 * When running tests in parallel, the beforeClass() and afterClass() seem to be invoked multiple times. 027 */ 028 private int testInstanceCounter; 029 030 private int securePort; 031 032 public int getSecurePort() { 033 return securePort; 034 } 035 036 public String getSecureUrl() { 037 return "https://localhost:" + getSecurePort(); 038 } 039 040 /** 041 * @return <code>true</code>, if this is the first invocation. <code>false</code> afterwards. 042 */ 043 public boolean beforeClass() throws Exception { 044 synchronized (cloudStoreServerMutex) { 045 final boolean first = testInstanceCounter++ == 0; 046 047 if (cloudStoreServerStopTimerTask != null) { 048 cloudStoreServerStopTimerTask.cancel(); 049 cloudStoreServerStopTimerTask = null; 050 } 051 052 if (cloudStoreServer == null) { 053 IOUtil.deleteDirectoryRecursively(ConfigDir.getInstance().getFile()); 054 055// securePort = 1024 + 1 + random.nextInt(10240); 056 securePort = getRandomAvailableServerPort(); 057 cloudStoreServer = createCloudStoreServer(); 058 cloudStoreServer.setSecurePort(securePort); 059 cloudStoreServerThread = new Thread(cloudStoreServer); 060 cloudStoreServerThread.setName("cloudStoreServerThread"); 061 cloudStoreServerThread.setDaemon(true); 062 cloudStoreServerThread.start(); 063 waitForServerToOpenSecurePort(); 064 } 065 066 return first; 067 } 068 } 069 070 private int getRandomAvailableServerPort() throws IOException { 071 final ServerSocket serverSocket = new ServerSocket(0); 072 final int port = serverSocket.getLocalPort(); 073 serverSocket.close(); 074 return port; 075 } 076 077 protected CloudStoreServer createCloudStoreServer() { 078 return new CloudStoreServer(); 079 } 080 081 private void waitForServerToOpenSecurePort() { 082 final long timeoutMillis = 3 * 60_000L; 083 final long begin = System.currentTimeMillis(); 084 while (true) { 085 try { 086 final Socket socket = new Socket("localhost", getSecurePort()); 087 socket.close(); 088 return; // success! 089 } catch (final Exception x) { 090 try { Thread.sleep(1000); } catch (final InterruptedException ie) { } 091 } 092 093 if (System.currentTimeMillis() - begin > timeoutMillis) 094 throw new IllegalStateException("Server did not start within timeout (ms): " + timeoutMillis); 095 } 096 } 097 098 /** 099 * @return <code>true</code>, if this is the last invocation. <code>false</code> before. 100 */ 101 public boolean afterClass() throws Exception { 102 synchronized (cloudStoreServerMutex) { 103 if (--testInstanceCounter > 0) 104 return false; 105 106 if (cloudStoreServerStopTimerTask == null) { 107 cloudStoreServerStopTimerTask = new TimerTask() { 108 @Override 109 public void run() { 110 synchronized (cloudStoreServerMutex) { 111 if (cloudStoreServer != null) { 112 cloudStoreServer.stop(); 113 cloudStoreServer = null; 114 cloudStoreServerStopTimerTask = null; 115 } 116 } 117 } 118 }; 119 cloudStoreServerStopTimer.schedule(cloudStoreServerStopTimerTask, 60000L); 120 } 121 122 return true; 123 } 124 } 125 126}