package org.lucares.pdbui; import java.io.IOException; import java.net.ConnectException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.SocketChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.time.Instant; import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import org.lucares.collections.LongList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.ObjectMapper; public class PdbTestUtil { private static final Logger LOGGER = LoggerFactory.getLogger(PdbTestUtil.class); static final Map POISON = new HashMap<>(); public static final void send(final String format, final Collection> entries, final int port) throws IOException, InterruptedException { switch (format) { case "csv": sendAsCsv(entries, port); break; case "json": sendAsJson(entries, port); break; default: throw new IllegalStateException("unhandled format: " + format); } } @SafeVarargs public static final void sendAsCsv(final int port, final Map... entries) throws IOException, InterruptedException { sendAsCsv(Arrays.asList(entries), port); } public static final void sendAsCsv(final Collection> entries, final int port) throws IOException, InterruptedException { final Set keys = entries.stream().map(Map::keySet).flatMap(Set::stream).collect(Collectors.toSet()); sendAsCsv(keys, entries, port); } public static final void sendAsCsv(final Collection keys, final Collection> entries, final int port) throws IOException, InterruptedException { final StringBuilder csv = new StringBuilder(); csv.append(String.join(",", keys)); csv.append("\n"); for (final Map entry : entries) { final List line = new ArrayList<>(); for (final String key : keys) { final String value = String.valueOf(entry.getOrDefault(key, "")); line.add(value); } csv.append(String.join(",", line)); csv.append("\n"); } System.out.println("sending: " + csv); send(csv.toString(), port); } @SafeVarargs public static final void sendAsJson(final int port, final Map... entries) throws IOException, InterruptedException { sendAsJson(Arrays.asList(entries), port); } public static final void sendAsJson(final Collection> entries, final int port) throws IOException, InterruptedException { final LinkedBlockingDeque> queue = new LinkedBlockingDeque<>(entries); queue.put(POISON); sendAsJson(queue, port); } public static final void sendAsJson(final BlockingQueue> aEntriesSupplier, final int port) throws IOException { final ObjectMapper mapper = new ObjectMapper(); final SocketChannel channel = connect(port); Map entry; while ((entry = aEntriesSupplier.poll()) != POISON) { final StringBuilder streamData = new StringBuilder(); streamData.append(mapper.writeValueAsString(entry)); streamData.append("\n"); final ByteBuffer src = ByteBuffer.wrap(streamData.toString().getBytes(StandardCharsets.UTF_8)); channel.write(src); } try { // ugly workaround: the channel was closed too early and not all // data was received TimeUnit.MILLISECONDS.sleep(10); } catch (final InterruptedException e) { throw new IllegalStateException(e); } channel.close(); LOGGER.trace("closed sender connection"); } public static final void send(final String data, final int port) throws IOException { final SocketChannel channel = connect(port); final StringBuilder streamData = new StringBuilder(); streamData.append(data); final ByteBuffer src = ByteBuffer.wrap(streamData.toString().getBytes(StandardCharsets.UTF_8)); channel.write(src); try { // ugly workaround: the channel was closed too early and not all // data was received TimeUnit.MILLISECONDS.sleep(10); } catch (final InterruptedException e) { throw new IllegalStateException(e); } channel.close(); LOGGER.trace("closed sender connection"); } public static void send(final Path file, final int port) throws IOException { final SocketChannel outputChannel = connect(port); try (final FileChannel inputChannel = FileChannel.open(file, StandardOpenOption.READ)) { inputChannel.transferTo(0, Long.MAX_VALUE, outputChannel); } try { // ugly workaround: the channel was closed too early and not all // data was received TimeUnit.MILLISECONDS.sleep(10); } catch (final InterruptedException e) { throw new IllegalStateException(e); } outputChannel.close(); LOGGER.trace("closed sender connection"); } private static SocketChannel connect(final int port) throws IOException { SocketChannel result = null; while (true) { try { result = SocketChannel.open(); result.configureBlocking(true); result.connect(new InetSocketAddress("127.0.0.1", port)); break; } catch (final ConnectException e) { // server socket not yet ready, it should be ready any time soon } } return result; } public static String timeValueLongListToString(final LongList timeValueLongList) { final StringBuilder result = new StringBuilder(); int i = 0; while (i < timeValueLongList.size()) { final OffsetDateTime time = OffsetDateTime.ofInstant(Instant.ofEpochMilli(timeValueLongList.get(i)), ZoneOffset.UTC); i++; final long value = timeValueLongList.get(i); i++; result.append(time.format(DateTimeFormatter.ISO_DATE_TIME)); result.append("="); result.append(value); result.append("\n"); } return result.toString(); } }