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}