/* Copyright (c) 2011 Danish Maritime Authority. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.maritimecloud.mms.tests; import static org.junit.Assert.assertTrue; import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.net.InetSocketAddress; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import net.maritimecloud.core.id.MaritimeId; import net.maritimecloud.internal.mms.messages.services.ClientInfo; import net.maritimecloud.mms.server.MmsServer; import net.maritimecloud.mms.server.MmsServerConfiguration; import net.maritimecloud.mms.server.connection.client.ClientManager; import net.maritimecloud.mms.server.rest.ClientManagerStatistics; import net.maritimecloud.net.mms.MmsClient; import net.maritimecloud.net.mms.MmsClientConfiguration; import net.maritimecloud.util.geometry.PositionReader; import net.maritimecloud.util.geometry.PositionTime; import org.junit.After; import org.junit.Before; import test.util.ProxyTester; /** * * @author Kasper Nielsen */ public abstract class AbstractNetworkTest { public static final MaritimeId ID1 = MaritimeId.create("mmsi:1"); public static final MaritimeId ID2 = MaritimeId.create("mmsi:2"); public static final MaritimeId ID3 = MaritimeId.create("mmsi:3"); public static final MaritimeId ID4 = MaritimeId.create("mmsi:4"); public static final MaritimeId ID5 = MaritimeId.create("mmsi:5"); public static final MaritimeId ID6 = MaritimeId.create("mmsi:6"); int clientPort; protected final ConcurrentHashMap<MaritimeId, MmsClient> clients = new ConcurrentHashMap<>(); ExecutorService es = Executors.newCachedThreadPool(); protected final ConcurrentHashMap<MaritimeId, LocationSup> locs = new ConcurrentHashMap<>(); ProxyTester pt; MmsServer si; final boolean useProxy; public AbstractNetworkTest() { this(false); } public AbstractNetworkTest(boolean useProxy) { this.useProxy = useProxy; } protected MmsClientConfiguration newBuilder(MaritimeId id) { MmsClientConfiguration b = MmsClientConfiguration.create(id); b.setHost("localhost:" + clientPort); return b; } protected MmsClient newClient() throws Exception { for (;;) { MaritimeId id = MaritimeId.create("mmsi:" + ThreadLocalRandom.current().nextInt(1000)); if (!clients.containsKey(id)) { return newClient(id); } } } protected MmsClient newClient(double lat, double lon) throws Exception { for (;;) { MaritimeId id = MaritimeId.create("mmsi:" + ThreadLocalRandom.current().nextInt(1000)); if (!clients.containsKey(id)) { return newClient(id, lat, lon); } } } protected MmsClient newClient(MaritimeId id) throws Exception { MmsClientConfiguration b = newBuilder(id); locs.put(id, new LocationSup()); MmsClient c = b.build(); clients.put(id, c); return c; } protected MmsClient newClient(MaritimeId id, double lat, double lon) throws Exception { MmsClientConfiguration b = newBuilder(id); LocationSup ls = new LocationSup(); b.setPositionReader(ls); locs.put(id, ls); setPosition(id, lat, lon); MmsClient c = b.build(); clients.put(id, c); return c; } protected MmsClient newClient(MmsClientConfiguration b) throws Exception { locs.put(b.getId(), new LocationSup()); MmsClient c = b.build(); clients.put(b.getId(), c); return c; } protected Future<MmsClient> newClientAsync(final MaritimeId id) throws Exception { final MmsClientConfiguration b = newBuilder(id); locs.put(id, new LocationSup()); return es.submit(new Callable<MmsClient>() { @Override public MmsClient call() throws Exception { MmsClient c = b.build(); clients.put(id, c); return c; } }); } protected Set<MmsClient> newClients(int count) throws Exception { HashSet<Future<MmsClient>> futures = new HashSet<>(); for (int j = 0; j < count; j++) { futures.add(newClientAsync(MaritimeId.create("mmsi:1234" + j))); } HashSet<MmsClient> result = new HashSet<>(); for (Future<MmsClient> f : futures) { result.add(f.get(3, TimeUnit.SECONDS)); } return result; } protected MaritimeId setPosition(MaritimeId id, double lat, double lon) { locs.get(id).lat = lat; locs.get(id).lon = lon; return id; } protected MmsClient setPosition(MmsClient pnc, double lat, double lon) { locs.get(pnc.getClientId()).lat = lat; locs.get(pnc.getClientId()).lon = lon; return pnc; } @Before public void setup() throws Exception { clientPort = ThreadLocalRandom.current().nextInt(40000, 50000); MmsServerConfiguration sc = new MmsServerConfiguration(); if (useProxy) { sc.setServerPort(12222); si = sc.build(); pt = new ProxyTester(new InetSocketAddress(clientPort), new InetSocketAddress(12222)); pt.start(); } else { sc.setServerPort(clientPort); si = sc.build(); } si.start().join(); } @After public void teardown() throws InterruptedException { for (final MmsClient c : clients.values()) { es.execute(new Runnable() { public void run() { c.shutdown(); } }); } for (MmsClient c : clients.values()) { if (!c.awaitTermination(2, TimeUnit.SECONDS)) { System.out.println("Failed to shutdown " + c.getClientId()); System.err.println(crunchifyGenerateThreadDump()); throw new AssertionError("Failed to shutdown " + c.getClientId()); } } clients.clear(); si.shutdown(); es.shutdown(); if (pt != null) { pt.shutdown(); } assertTrue(es.awaitTermination(10, TimeUnit.SECONDS)); assertTrue(si.awaitTerminated(10, TimeUnit.SECONDS)); } public List<ClientInfo> clients(MmsServer server) { ClientManager ac = server.getService(ClientManager.class); return new ClientManagerStatistics(ac).getAllClients().getClients(); } public static String crunchifyGenerateThreadDump() { final StringBuilder dump = new StringBuilder(); final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); final ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(threadMXBean.getAllThreadIds(), 100); for (ThreadInfo threadInfo : threadInfos) { dump.append('"'); dump.append(threadInfo.getThreadName()); dump.append("\" "); final Thread.State state = threadInfo.getThreadState(); dump.append("\n java.lang.Thread.State: "); dump.append(state); final StackTraceElement[] stackTraceElements = threadInfo.getStackTrace(); for (final StackTraceElement stackTraceElement : stackTraceElements) { dump.append("\n at "); dump.append(stackTraceElement); } dump.append("\n\n"); } return dump.toString(); } static class LocationSup extends PositionReader { double lat; double lon; /** {@inheritDoc} */ @Override public PositionTime getCurrentPosition() { return PositionTime.create(lat, lon, System.currentTimeMillis()); } } }