From 925ee330df285573b1ef1266db70d2e1d81710d4 Mon Sep 17 00:00:00 2001 From: Edward Harman Date: Mon, 7 Feb 2022 12:40:20 -0500 Subject: [PATCH 1/3] begin migration to Java 17. Switch formatting to Google Java Format to avoid npm dependency --- .sdkmanrc | 2 +- app/build.gradle | 11 +- .../org/ethelred/minecraft/webhook/App.java | 17 +- .../minecraft/webhook/BackCompatUrlSetup.java | 23 +- .../minecraft/webhook/ContainerId.java | 3 +- .../minecraft/webhook/DefaultDocker.java | 15 +- .../webhook/DiscordWebhookSender.java | 204 ++++++++---------- .../minecraft/webhook/JsonSender.java | 29 ++- .../webhook/MinecraftServerEvent.java | 150 ++++++------- .../webhook/MinecraftServerEventListener.java | 76 +++---- .../ethelred/minecraft/webhook/Monitor.java | 89 ++++---- .../ethelred/minecraft/webhook/Options.java | 37 ++-- .../ethelred/minecraft/webhook/Sender.java | 6 +- .../webhook/SenderConfiguration.java | 78 ++++--- .../ethelred/minecraft/webhook/Tailer.java | 177 +++++++-------- .../groovy/ethelred.java-conventions.gradle | 14 +- 16 files changed, 419 insertions(+), 512 deletions(-) diff --git a/.sdkmanrc b/.sdkmanrc index e8fda62..98e2275 100644 --- a/.sdkmanrc +++ b/.sdkmanrc @@ -1,3 +1,3 @@ # Enable auto-env through the sdkman_auto_env config # Add key=value pairs of SDKs to use below -java=21.3.0.r11-grl +java=21.3.0.r17-grl diff --git a/app/build.gradle b/app/build.gradle index 9f72332..8367281 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,14 +10,7 @@ plugins { id("io.micronaut.application") version "3.2.0" } -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - vendor = JvmVendorSpec.GRAAL_VM - } -} - -version = "0.3.1" +version = "0.3.2" repositories { mavenCentral() @@ -75,7 +68,7 @@ tasks.named('test') { } tasks.named("dockerfile") { - baseImage = "ghcr.io/graalvm/graalvm-ce:java11-21.3.0" + baseImage = "ghcr.io/graalvm/graalvm-ce:java17-21.3.0" instruction """RUN touch /config.yml""" } diff --git a/app/src/main/java/org/ethelred/minecraft/webhook/App.java b/app/src/main/java/org/ethelred/minecraft/webhook/App.java index aa67872..6aec2ad 100644 --- a/app/src/main/java/org/ethelred/minecraft/webhook/App.java +++ b/app/src/main/java/org/ethelred/minecraft/webhook/App.java @@ -7,16 +7,11 @@ @Singleton public class App { - public static void main(String[] args) { - // tell Micronaut to look for config in known paths of the docker image - System.setProperty( - "micronaut.config.files", - "/config.yml" // in root of docker image + public static void main(String[] args) { + // tell Micronaut to look for config in known paths of the docker image + System.setProperty( + "micronaut.config.files", "/config.yml" // in root of docker image ); - Micronaut - .build(args) - .mainClass(App.class) - .defaultEnvironments("dev") - .start(); - } + Micronaut.build(args).mainClass(App.class).defaultEnvironments("dev").start(); + } } diff --git a/app/src/main/java/org/ethelred/minecraft/webhook/BackCompatUrlSetup.java b/app/src/main/java/org/ethelred/minecraft/webhook/BackCompatUrlSetup.java index 796dcd9..ec54bdd 100644 --- a/app/src/main/java/org/ethelred/minecraft/webhook/BackCompatUrlSetup.java +++ b/app/src/main/java/org/ethelred/minecraft/webhook/BackCompatUrlSetup.java @@ -15,17 +15,16 @@ @Requires(property = "mc-webhook.webhook-url") public class BackCompatUrlSetup extends MinecraftServerEventListener { - public BackCompatUrlSetup( - BeanContext context, - ConversionService conversionService, - @Property(name = "mc-webhook.webhook-url") URL url - ) { - super(context, conversionService, getConfiguration(url)); - } + public BackCompatUrlSetup( + BeanContext context, + ConversionService conversionService, + @Property(name = "mc-webhook.webhook-url") URL url) { + super(context, conversionService, getConfiguration(url)); + } - private static SenderConfiguration getConfiguration(URL url) { - var config = new SenderConfiguration(); - config.setUrl(url); // defaults are ok for other properties - return config; - } + private static SenderConfiguration getConfiguration(URL url) { + var config = new SenderConfiguration(); + config.setUrl(url); // defaults are ok for other properties + return config; + } } diff --git a/app/src/main/java/org/ethelred/minecraft/webhook/ContainerId.java b/app/src/main/java/org/ethelred/minecraft/webhook/ContainerId.java index e8bb0d5..f1ac4b7 100644 --- a/app/src/main/java/org/ethelred/minecraft/webhook/ContainerId.java +++ b/app/src/main/java/org/ethelred/minecraft/webhook/ContainerId.java @@ -9,5 +9,4 @@ @Qualifier @Documented @Retention(RetentionPolicy.RUNTIME) -public @interface ContainerId { -} +public @interface ContainerId {} diff --git a/app/src/main/java/org/ethelred/minecraft/webhook/DefaultDocker.java b/app/src/main/java/org/ethelred/minecraft/webhook/DefaultDocker.java index c95875c..0fc1ad2 100644 --- a/app/src/main/java/org/ethelred/minecraft/webhook/DefaultDocker.java +++ b/app/src/main/java/org/ethelred/minecraft/webhook/DefaultDocker.java @@ -11,15 +11,14 @@ @Factory public class DefaultDocker { - @Singleton - public DockerClient docker() { - var dockerCC = DefaultDockerClientConfig - .createDefaultConfigBuilder() - .build(); - var http = new ApacheDockerHttpClient.Builder() + @Singleton + public DockerClient docker() { + var dockerCC = DefaultDockerClientConfig.createDefaultConfigBuilder().build(); + var http = + new ApacheDockerHttpClient.Builder() .dockerHost(dockerCC.getDockerHost()) .sslConfig(dockerCC.getSSLConfig()) .build(); - return DockerClientImpl.getInstance(dockerCC, http); - } + return DockerClientImpl.getInstance(dockerCC, http); + } } diff --git a/app/src/main/java/org/ethelred/minecraft/webhook/DiscordWebhookSender.java b/app/src/main/java/org/ethelred/minecraft/webhook/DiscordWebhookSender.java index 52d8d2b..74c5926 100644 --- a/app/src/main/java/org/ethelred/minecraft/webhook/DiscordWebhookSender.java +++ b/app/src/main/java/org/ethelred/minecraft/webhook/DiscordWebhookSender.java @@ -24,125 +24,105 @@ @Named("discord") public class DiscordWebhookSender implements Sender { - private static final long DEFAULT_DELAY = 3_000; - private static final Logger LOGGER = LogManager.getLogger( - DiscordWebhookSender.class - ); + private static final long DEFAULT_DELAY = 3_000; + private static final Logger LOGGER = LogManager.getLogger(DiscordWebhookSender.class); - private final URI webhook; - private final HttpClient client; - private final ScheduledExecutorService scheduler; - private final BlockingQueue waiting; + private final URI webhook; + private final HttpClient client; + private final ScheduledExecutorService scheduler; + private final BlockingQueue waiting; - @Inject - public DiscordWebhookSender(@Parameter URL webhookUrl) { - try { - this.webhook = webhookUrl.toURI(); - } catch (URISyntaxException e) { - throw new IllegalArgumentException(e); - } - this.client = HttpClient.newBuilder().build(); - this.scheduler = Executors.newSingleThreadScheduledExecutor(); - this.waiting = new ArrayBlockingQueue<>(64); - _scheduleNext(0L); - LOGGER.info( - "DiscordWebhookSender initialized with URL {}", - this.webhook - ); + @Inject + public DiscordWebhookSender(@Parameter URL webhookUrl) { + try { + this.webhook = webhookUrl.toURI(); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e); } + this.client = HttpClient.newBuilder().build(); + this.scheduler = Executors.newSingleThreadScheduledExecutor(); + this.waiting = new ArrayBlockingQueue<>(64); + _scheduleNext(0L); + LOGGER.info("DiscordWebhookSender initialized with URL {}", this.webhook); + } - private void _scheduleNext(long delay) { - LOGGER.debug("_scheduleNext({})", delay); - scheduler.schedule( - () -> { - try { - _sendMessage(waiting.take()); - } catch (InterruptedException e) { - _scheduleNext(DEFAULT_DELAY); - } - }, - delay, - TimeUnit.MILLISECONDS - ); - } + private void _scheduleNext(long delay) { + LOGGER.debug("_scheduleNext({})", delay); + scheduler.schedule( + () -> { + try { + _sendMessage(waiting.take()); + } catch (InterruptedException e) { + _scheduleNext(DEFAULT_DELAY); + } + }, + delay, + TimeUnit.MILLISECONDS); + } - private void _sendMessage(String message) { - LOGGER.debug(message); - long delay = 0; - try { - // this works for Discord, not sure if it's compatible with other systems - var request = HttpRequest - .newBuilder(webhook) - .header("Content-Type", "application/json") - .POST( - HttpRequest.BodyPublishers.ofString( - String.format("{\"content\":\"%s\"}", message) - ) - ) - .build(); - var response = client.send( - request, - HttpResponse.BodyHandlers.ofString() - ); - LOGGER.debug(response::body); - switch (response.statusCode()) { - case 401: // Unauthorized - case 403: // Forbidden - // Most likely the webhook URL is wrong and this will never succeed - System.out.printf( - "Discord response %d %s%nDouble check your webhook URL %s%nSystem will exit", - response.statusCode(), - response.body(), - webhook - ); - System.exit(4); - break; - case 503: // Service Unavailable - case 429: // Rate Limit https://discord.com/developers/docs/topics/rate-limits - // Set a delay - var retryValue = response - .headers() - .firstValue("Retry-After"); - delay = _delayFromHeaderValue(retryValue); - case 200: - case 204: // no content - // awesome - break; - default: - throw new IOException( - String.format( - "Unexpected response status %d%n%s", - response.statusCode(), - response.body() - ) - ); - } - } catch (IOException | InterruptedException e) { - // can't tell at this point whether the message was sent or not - just give up and log - LOGGER.error("Exception in _sendMessage", e); - delay = DEFAULT_DELAY; - } finally { - _scheduleNext(delay); - } + private void _sendMessage(String message) { + LOGGER.debug(message); + long delay = 0; + try { + // this works for Discord, not sure if it's compatible with other systems + var request = + HttpRequest.newBuilder(webhook) + .header("Content-Type", "application/json") + .POST( + HttpRequest.BodyPublishers.ofString( + String.format("{\"content\":\"%s\"}", message))) + .build(); + var response = client.send(request, HttpResponse.BodyHandlers.ofString()); + LOGGER.debug(response::body); + switch (response.statusCode()) { + case 401: // Unauthorized + case 403: // Forbidden + // Most likely the webhook URL is wrong and this will never succeed + System.out.printf( + "Discord response %d %s%nDouble check your webhook URL %s%nSystem will exit", + response.statusCode(), response.body(), webhook); + System.exit(4); + break; + case 503: // Service Unavailable + case 429: // Rate Limit https://discord.com/developers/docs/topics/rate-limits + // Set a delay + var retryValue = response.headers().firstValue("Retry-After"); + delay = _delayFromHeaderValue(retryValue); + case 200: + case 204: // no content + // awesome + break; + default: + throw new IOException( + String.format( + "Unexpected response status %d%n%s", response.statusCode(), response.body())); + } + } catch (IOException | InterruptedException e) { + // can't tell at this point whether the message was sent or not - just give up and log + LOGGER.error("Exception in _sendMessage", e); + delay = DEFAULT_DELAY; + } finally { + _scheduleNext(delay); } + } - @SuppressWarnings("OptionalUsedAsFieldOrParameterType") - private long _delayFromHeaderValue(Optional retryValue) { - if (retryValue.isPresent()) { - try { - return Long.parseLong(retryValue.get()) * 1000; // header is in seconds - } catch (NumberFormatException e) { - // TODO is date format used in this API? - LOGGER.debug(retryValue.get(), e); - } - } - return DEFAULT_DELAY; + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") + private long _delayFromHeaderValue(Optional retryValue) { + if (retryValue.isPresent()) { + try { + return Long.parseLong(retryValue.get()) * 1000; // header is in seconds + } catch (NumberFormatException e) { + // TODO is date format used in this API? + LOGGER.debug(retryValue.get(), e); + } } + return DEFAULT_DELAY; + } - @Override - public void sendMessage(MinecraftServerEvent event, String message) { - LOGGER.debug("sendMessage({})", message.trim()); - //noinspection ResultOfMethodCallIgnored - waiting.offer(message.trim()); - } + @Override + public void sendMessage(MinecraftServerEvent event, String message) { + LOGGER.debug("sendMessage({})", message.trim()); + //noinspection ResultOfMethodCallIgnored + waiting.offer(message.trim()); + } } diff --git a/app/src/main/java/org/ethelred/minecraft/webhook/JsonSender.java b/app/src/main/java/org/ethelred/minecraft/webhook/JsonSender.java index 6188cfb..06848d6 100644 --- a/app/src/main/java/org/ethelred/minecraft/webhook/JsonSender.java +++ b/app/src/main/java/org/ethelred/minecraft/webhook/JsonSender.java @@ -17,22 +17,21 @@ @Named("json") public class JsonSender implements Sender { - private static final Logger LOGGER = LogManager.getLogger(); + private static final Logger LOGGER = LogManager.getLogger(); - private final BlockingHttpClient client; - private final URI url; + private final BlockingHttpClient client; + private final URI url; - public JsonSender(HttpClient client, @Parameter URL url) - throws URISyntaxException { - this.client = client.toBlocking(); - this.url = url.toURI(); - } + public JsonSender(HttpClient client, @Parameter URL url) throws URISyntaxException { + this.client = client.toBlocking(); + this.url = url.toURI(); + } - @Override - public void sendMessage(MinecraftServerEvent event, String message) { - LOGGER.debug("Send message {}", event); - var request = HttpRequest.POST(url, event); - var response = client.exchange(request); - LOGGER.debug(response); - } + @Override + public void sendMessage(MinecraftServerEvent event, String message) { + LOGGER.debug("Send message {}", event); + var request = HttpRequest.POST(url, event); + var response = client.exchange(request); + LOGGER.debug(response); + } } diff --git a/app/src/main/java/org/ethelred/minecraft/webhook/MinecraftServerEvent.java b/app/src/main/java/org/ethelred/minecraft/webhook/MinecraftServerEvent.java index 246bc11..5a36555 100644 --- a/app/src/main/java/org/ethelred/minecraft/webhook/MinecraftServerEvent.java +++ b/app/src/main/java/org/ethelred/minecraft/webhook/MinecraftServerEvent.java @@ -8,83 +8,75 @@ @Introspected public class MinecraftServerEvent { - public MinecraftServerEvent( - @NonNull Type type, - @NonNull String containerId, - @NonNull String containerName, - @NonNull String worldName - ) { - this(type, containerId, containerName, worldName, null, null); - } - - public MinecraftServerEvent( - @NonNull Type type, - @NonNull String containerId, - @NonNull String containerName, - @NonNull String worldName, - @Nullable String playerName, - @Nullable String playerXuid - ) { - this.type = type; - this.containerId = containerId; - this.containerName = containerName; - this.worldName = worldName; - this.playerName = playerName; - this.playerXuid = playerXuid; - } - - enum Type { - PLAYER_CONNECTED, - PLAYER_DISCONNECTED, - SERVER_STARTED, - SERVER_STOPPED, - } - - @NonNull - public Type getType() { - return type; - } - - @NonNull - public String getContainerId() { - return containerId; - } - - @NonNull - public String getContainerName() { - return containerName; - } - - @NonNull - public String getWorldName() { - return worldName; - } - - @Nullable - public String getPlayerName() { - return playerName; - } - - @Nullable - public String getPlayerXuid() { - return playerXuid; - } - - @NonNull - private final Type type; - - @NonNull - private final String containerId; - - @NonNull - private final String containerName; - - @NonNull - private final String worldName; - - @Nullable - private final String playerName; - - @Nullable - private final String playerXuid; + public MinecraftServerEvent( + @NonNull Type type, + @NonNull String containerId, + @NonNull String containerName, + @NonNull String worldName) { + this(type, containerId, containerName, worldName, null, null); + } + + public MinecraftServerEvent( + @NonNull Type type, + @NonNull String containerId, + @NonNull String containerName, + @NonNull String worldName, + @Nullable String playerName, + @Nullable String playerXuid) { + this.type = type; + this.containerId = containerId; + this.containerName = containerName; + this.worldName = worldName; + this.playerName = playerName; + this.playerXuid = playerXuid; + } + + enum Type { + PLAYER_CONNECTED, + PLAYER_DISCONNECTED, + SERVER_STARTED, + SERVER_STOPPED, + } + + @NonNull + public Type getType() { + return type; + } + + @NonNull + public String getContainerId() { + return containerId; + } + + @NonNull + public String getContainerName() { + return containerName; + } + + @NonNull + public String getWorldName() { + return worldName; + } + + @Nullable + public String getPlayerName() { + return playerName; + } + + @Nullable + public String getPlayerXuid() { + return playerXuid; + } + + @NonNull private final Type type; + + @NonNull private final String containerId; + + @NonNull private final String containerName; + + @NonNull private final String worldName; + + @Nullable private final String playerName; + + @Nullable private final String playerXuid; } diff --git a/app/src/main/java/org/ethelred/minecraft/webhook/MinecraftServerEventListener.java b/app/src/main/java/org/ethelred/minecraft/webhook/MinecraftServerEventListener.java index cf5d77c..215e803 100644 --- a/app/src/main/java/org/ethelred/minecraft/webhook/MinecraftServerEventListener.java +++ b/app/src/main/java/org/ethelred/minecraft/webhook/MinecraftServerEventListener.java @@ -17,52 +17,42 @@ public class MinecraftServerEventListener implements ApplicationEventListener { - private static final BeanIntrospection eventIntrospection = BeanIntrospection.getIntrospection( - MinecraftServerEvent.class - ); + private static final BeanIntrospection eventIntrospection = + BeanIntrospection.getIntrospection(MinecraftServerEvent.class); - private final SenderConfiguration configuration; - private final Sender sender; - private final ConversionService conversionService; + private final SenderConfiguration configuration; + private final Sender sender; + private final ConversionService conversionService; - public MinecraftServerEventListener( - BeanContext beanContext, - ConversionService conversionService, - SenderConfiguration configuration - ) { - this.conversionService = conversionService; - this.configuration = configuration; - this.sender = - beanContext.createBean( - Sender.class, - Qualifiers.byName(configuration.getType()), - configuration.getUrl() - ); - } + public MinecraftServerEventListener( + BeanContext beanContext, + ConversionService conversionService, + SenderConfiguration configuration) { + this.conversionService = conversionService; + this.configuration = configuration; + this.sender = + beanContext.createBean( + Sender.class, Qualifiers.byName(configuration.getType()), configuration.getUrl()); + } - @Async - @Override - public void onApplicationEvent(MinecraftServerEvent event) { - if (configuration.getEvents().containsKey(event.getType())) { - var substitutor = new StringSubstitutor( - _eventLookup(event), - "%", - "%", - '\\' - ); - var messageFormat = configuration.getEvents().get(event.getType()); - var message = substitutor.replace(messageFormat); - sender.sendMessage(event, message); - } + @Async + @Override + public void onApplicationEvent(MinecraftServerEvent event) { + if (configuration.getEvents().containsKey(event.getType())) { + var substitutor = new StringSubstitutor(_eventLookup(event), "%", "%", '\\'); + var messageFormat = configuration.getEvents().get(event.getType()); + var message = substitutor.replace(messageFormat); + sender.sendMessage(event, message); } + } - private StringLookup _eventLookup(MinecraftServerEvent event) { - return key -> { - var property = eventIntrospection.getProperty(key); - return property - .map(p -> p.get(event)) - .flatMap(o -> conversionService.convert(o, String.class)) - .orElse(null); - }; - } + private StringLookup _eventLookup(MinecraftServerEvent event) { + return key -> { + var property = eventIntrospection.getProperty(key); + return property + .map(p -> p.get(event)) + .flatMap(o -> conversionService.convert(o, String.class)) + .orElse(null); + }; + } } diff --git a/app/src/main/java/org/ethelred/minecraft/webhook/Monitor.java b/app/src/main/java/org/ethelred/minecraft/webhook/Monitor.java index 323e13e..b12afd7 100644 --- a/app/src/main/java/org/ethelred/minecraft/webhook/Monitor.java +++ b/app/src/main/java/org/ethelred/minecraft/webhook/Monitor.java @@ -14,61 +14,46 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -/** - * Lists docker containers to check for new/removed ones. Creates Tailers - */ +/** Lists docker containers to check for new/removed ones. Creates Tailers */ @Context public class Monitor { - private static final Logger LOGGER = LogManager.getLogger(Monitor.class); - - private final DockerClient docker; - private final Collection imageNames; - private final Instant startTime; - private final ApplicationContext applicationContext; - - @Inject - public Monitor( - ApplicationContext applicationContext, - DockerClient docker, - Options options - ) { - this.applicationContext = applicationContext; - this.docker = docker; - this.imageNames = options.getImageNames(); - this.startTime = Instant.now(); - LOGGER.debug("Constructed"); - } - - private final Map tails = new ConcurrentHashMap<>(); - - @Scheduled(fixedRate = "${mc-webhook.options.monitor.rate:5s}") - public void checkForContainers() { - LOGGER.debug("Checking for containers"); - docker - .listContainersCmd() - .withAncestorFilter(imageNames) - .exec() - .forEach(this::_checkContainer); - } - - private void _checkContainer(Container container) { - var containerId = container.getId(); - if (!tails.containsKey(containerId)) { - LOGGER.debug("Adding container {}", container); - tails.putIfAbsent( - containerId, - applicationContext.createBean( - Tailer.class, - containerId, - container.getNames(), - onComplete(containerId) - ) - ); - } + private static final Logger LOGGER = LogManager.getLogger(Monitor.class); + + private final DockerClient docker; + private final Collection imageNames; + private final Instant startTime; + private final ApplicationContext applicationContext; + + @Inject + public Monitor(ApplicationContext applicationContext, DockerClient docker, Options options) { + this.applicationContext = applicationContext; + this.docker = docker; + this.imageNames = options.imageNames(); + this.startTime = Instant.now(); + LOGGER.debug("Constructed"); + } + + private final Map tails = new ConcurrentHashMap<>(); + + @Scheduled(fixedRate = "${mc-webhook.options.monitor.rate:5s}") + public void checkForContainers() { + LOGGER.debug("Checking for containers"); + docker.listContainersCmd().withAncestorFilter(imageNames).exec().forEach(this::_checkContainer); + } + + private void _checkContainer(Container container) { + var containerId = container.getId(); + if (!tails.containsKey(containerId)) { + LOGGER.debug("Adding container {}", container); + tails.putIfAbsent( + containerId, + applicationContext.createBean( + Tailer.class, containerId, container.getNames(), onComplete(containerId))); } + } - private Runnable onComplete(String containerId) { - return () -> tails.remove(containerId); - } + private Runnable onComplete(String containerId) { + return () -> tails.remove(containerId); + } } diff --git a/app/src/main/java/org/ethelred/minecraft/webhook/Options.java b/app/src/main/java/org/ethelred/minecraft/webhook/Options.java index cf8bbe5..d07627e 100644 --- a/app/src/main/java/org/ethelred/minecraft/webhook/Options.java +++ b/app/src/main/java/org/ethelred/minecraft/webhook/Options.java @@ -3,38 +3,25 @@ import io.micronaut.context.annotation.ConfigurationProperties; import io.micronaut.context.annotation.Context; +import io.micronaut.core.annotation.Nullable; import java.util.HashSet; import java.util.Set; -import javax.validation.constraints.NotEmpty; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -/** - * options for app - */ +/** general options for app */ @Context // ensure options are validated early @ConfigurationProperties("mc-webhook") -public class Options { +public record Options(@Nullable Set imageNames) { - private static final Logger LOGGER = LogManager.getLogger(); + private static final Set DEFAULT_IMAGE_NAMES = Set.of("itzg/minecraft-bedrock-server"); - private static final Set DEFAULT_IMAGE_NAMES = Set.of( - "itzg/minecraft-bedrock-server" - ); - - @NotEmpty - public Set getImageNames() { - return imageNames.isEmpty() ? DEFAULT_IMAGE_NAMES : imageNames; - } - - @SuppressWarnings("unused") - public void setImageNames(Set imageNames) { - this.imageNames = imageNames; - } - - public void setImageName(String imageName) { - this.imageNames.add(imageName); + public Options { + if (imageNames == null || imageNames.isEmpty()) { + imageNames = new HashSet<>(DEFAULT_IMAGE_NAMES); } + } - private Set imageNames = new HashSet<>(); + public void setImageName(String imageName) { + this.imageNames.clear(); + this.imageNames.add(imageName); + } } diff --git a/app/src/main/java/org/ethelred/minecraft/webhook/Sender.java b/app/src/main/java/org/ethelred/minecraft/webhook/Sender.java index 5f4c253..767616f 100644 --- a/app/src/main/java/org/ethelred/minecraft/webhook/Sender.java +++ b/app/src/main/java/org/ethelred/minecraft/webhook/Sender.java @@ -1,9 +1,7 @@ /* (C) Edward Harman 2022 */ package org.ethelred.minecraft.webhook; -/** - * I can't believe it's not Consumer - */ +/** I can't believe it's not Consumer */ public interface Sender { - void sendMessage(MinecraftServerEvent event, String message); + void sendMessage(MinecraftServerEvent event, String message); } diff --git a/app/src/main/java/org/ethelred/minecraft/webhook/SenderConfiguration.java b/app/src/main/java/org/ethelred/minecraft/webhook/SenderConfiguration.java index 0e88e8b..0df50ff 100644 --- a/app/src/main/java/org/ethelred/minecraft/webhook/SenderConfiguration.java +++ b/app/src/main/java/org/ethelred/minecraft/webhook/SenderConfiguration.java @@ -10,44 +10,42 @@ @EachProperty("mc-webhook.webhooks") public class SenderConfiguration { - @NotBlank - private String type = "discord"; - - @NotNull - private URL url; - - private Map events = Map.of( - MinecraftServerEvent.Type.SERVER_STARTED, - "World %worldName% starting on %containerName%", - MinecraftServerEvent.Type.SERVER_STOPPED, - "World %worldName% stopping on %containerName%", - MinecraftServerEvent.Type.PLAYER_CONNECTED, - "%playerName% connected to %worldName%", - MinecraftServerEvent.Type.PLAYER_DISCONNECTED, - "%playerName% disconnected from %worldName%" - ); - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public URL getUrl() { - return url; - } - - public void setUrl(URL url) { - this.url = url; - } - - public Map getEvents() { - return events; - } - - public void setEvents(Map events) { - this.events = events; - } + @NotBlank private String type = "discord"; + + @NotNull private URL url; + + private Map events = + Map.of( + MinecraftServerEvent.Type.SERVER_STARTED, + "World %worldName% starting on %containerName%", + MinecraftServerEvent.Type.SERVER_STOPPED, + "World %worldName% stopping on %containerName%", + MinecraftServerEvent.Type.PLAYER_CONNECTED, + "%playerName% connected to %worldName%", + MinecraftServerEvent.Type.PLAYER_DISCONNECTED, + "%playerName% disconnected from %worldName%"); + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public URL getUrl() { + return url; + } + + public void setUrl(URL url) { + this.url = url; + } + + public Map getEvents() { + return events; + } + + public void setEvents(Map events) { + this.events = events; + } } diff --git a/app/src/main/java/org/ethelred/minecraft/webhook/Tailer.java b/app/src/main/java/org/ethelred/minecraft/webhook/Tailer.java index 3a10ba7..eb5b7c7 100644 --- a/app/src/main/java/org/ethelred/minecraft/webhook/Tailer.java +++ b/app/src/main/java/org/ethelred/minecraft/webhook/Tailer.java @@ -11,116 +11,97 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -/** - * tail container logs - */ +/** tail container logs */ public class Tailer { - private static final Logger LOGGER = LogManager.getLogger(); + private static final Logger LOGGER = LogManager.getLogger(); - private static final Pattern levelName = Pattern.compile("Level Name:(.*)"); - /* -[INFO] Player connected: Foxer191, xuid: 2535428717109723 -[INFO] Player disconnected: Foxer191, xuid: 2535428717109723 - */ - private static final Pattern playerEvent = Pattern.compile( - " Player ([^ ]*connected): (.*), xuid: (\\d+)" - ); - private final Runnable completionCallback; - private final ApplicationEventPublisher eventPublisher; - private final String containerId; - private final String containerName; - private volatile String worldName = "Unknown"; + private static final Pattern levelName = Pattern.compile("Level Name:(.*)"); + /* + [INFO] Player connected: Foxer191, xuid: 2535428717109723 + [INFO] Player disconnected: Foxer191, xuid: 2535428717109723 + */ + private static final Pattern playerEvent = + Pattern.compile(" Player ([^ ]*connected): (.*), xuid: (\\d+)"); + private final Runnable completionCallback; + private final ApplicationEventPublisher eventPublisher; + private final String containerId; + private final String containerName; + private volatile String worldName = "Unknown"; - @Inject - public Tailer( - ApplicationEventPublisher eventPublisher, - DockerClient docker, - @Parameter String containerId, - @Parameter String[] containerNames, - @Parameter Runnable completionCallback - ) { - this.eventPublisher = eventPublisher; - this.completionCallback = completionCallback; - this.containerId = containerId; - this.containerName = String.join(",", containerNames); - LOGGER.info("Tailer is starting for {}", containerName); + @Inject + public Tailer( + ApplicationEventPublisher eventPublisher, + DockerClient docker, + @Parameter String containerId, + @Parameter String[] containerNames, + @Parameter Runnable completionCallback) { + this.eventPublisher = eventPublisher; + this.completionCallback = completionCallback; + this.containerId = containerId; + this.containerName = String.join(",", containerNames); + LOGGER.info("Tailer is starting for {}", containerName); - _initial(docker, containerId); - _follow(docker, containerId); - } + _initial(docker, containerId); + _follow(docker, containerId); + } - private void _initial(DockerClient docker, String containerId) { - docker - .logContainerCmd(containerId) - .withStdOut(true) - .exec(new InitialCallback()); - } + private void _initial(DockerClient docker, String containerId) { + docker.logContainerCmd(containerId).withStdOut(true).exec(new InitialCallback()); + } - private void _follow(DockerClient docker, String containerId) { - docker - .logContainerCmd(containerId) - .withStdOut(true) - .withTail(0) - .withFollowStream(true) - .exec(new FollowCallback()); - } + private void _follow(DockerClient docker, String containerId) { + docker + .logContainerCmd(containerId) + .withStdOut(true) + .withTail(0) + .withFollowStream(true) + .exec(new FollowCallback()); + } - private class InitialCallback extends ResultCallback.Adapter { + private class InitialCallback extends ResultCallback.Adapter { - @Override - public void onNext(Frame frame) { - var matcher = levelName.matcher(frame.toString()); - if (matcher.find()) { - worldName = matcher.group(1).trim(); - LOGGER.debug("Found world name {}", worldName); - eventPublisher.publishEventAsync( - new MinecraftServerEvent( - MinecraftServerEvent.Type.SERVER_STARTED, - containerId, - containerName, - worldName - ) - ); - } - } + @Override + public void onNext(Frame frame) { + var matcher = levelName.matcher(frame.toString()); + if (matcher.find()) { + worldName = matcher.group(1).trim(); + LOGGER.debug("Found world name {}", worldName); + eventPublisher.publishEventAsync( + new MinecraftServerEvent( + MinecraftServerEvent.Type.SERVER_STARTED, containerId, containerName, worldName)); + } } + } - private class FollowCallback extends ResultCallback.Adapter { + private class FollowCallback extends ResultCallback.Adapter { - @Override - public void onNext(Frame frame) { - var matcher = playerEvent.matcher(frame.toString()); - if (matcher.find()) { - var connect = "connected".equals(matcher.group(1)); - var player = matcher.group(2).trim(); - var xuid = matcher.group(3).trim(); - eventPublisher.publishEventAsync( - new MinecraftServerEvent( - connect - ? MinecraftServerEvent.Type.PLAYER_CONNECTED - : MinecraftServerEvent.Type.PLAYER_DISCONNECTED, - containerId, - containerName, - worldName, - player, - xuid - ) - ); - } - } + @Override + public void onNext(Frame frame) { + var matcher = playerEvent.matcher(frame.toString()); + if (matcher.find()) { + var connect = "connected".equals(matcher.group(1)); + var player = matcher.group(2).trim(); + var xuid = matcher.group(3).trim(); + eventPublisher.publishEventAsync( + new MinecraftServerEvent( + connect + ? MinecraftServerEvent.Type.PLAYER_CONNECTED + : MinecraftServerEvent.Type.PLAYER_DISCONNECTED, + containerId, + containerName, + worldName, + player, + xuid)); + } + } - @Override - public void onComplete() { - eventPublisher.publishEventAsync( - new MinecraftServerEvent( - MinecraftServerEvent.Type.SERVER_STOPPED, - containerId, - containerName, - worldName - ) - ); - completionCallback.run(); - } + @Override + public void onComplete() { + eventPublisher.publishEventAsync( + new MinecraftServerEvent( + MinecraftServerEvent.Type.SERVER_STOPPED, containerId, containerName, worldName)); + completionCallback.run(); } + } } diff --git a/buildSrc/src/main/groovy/ethelred.java-conventions.gradle b/buildSrc/src/main/groovy/ethelred.java-conventions.gradle index e128e82..19ccc09 100644 --- a/buildSrc/src/main/groovy/ethelred.java-conventions.gradle +++ b/buildSrc/src/main/groovy/ethelred.java-conventions.gradle @@ -4,6 +4,18 @@ plugins { id "com.github.jakemarsden.git-hooks" } +//java { +// toolchain { +// languageVersion = JavaLanguageVersion.of(17) +// vendor = JvmVendorSpec.GRAAL_VM +// } +//} +java { + sourceCompatibility = JavaVersion.toVersion("17") + targetCompatibility = JavaVersion.toVersion("17") +} + + spotless { format 'misc', { target '*.md', '.gitignore' @@ -14,7 +26,7 @@ spotless { java { importOrder() removeUnusedImports() - prettier(['prettier': '2.5.1', 'prettier-plugin-java': '1.6.1']).config(['parser': 'java', 'tabWidth': 4]) + googleJavaFormat() licenseHeader('/* (C) Edward Harman $YEAR */').updateYearWithLatest(true) } groovyGradle { From f7d3874fb9d0efa1a6eb6d18991b003ca609de87 Mon Sep 17 00:00:00 2001 From: Edward Harman Date: Mon, 7 Feb 2022 13:02:57 -0500 Subject: [PATCH 2/3] event class to record --- .../webhook/MinecraftServerEvent.java | 65 ++----------------- .../webhook/MinecraftServerEventListener.java | 4 +- 2 files changed, 9 insertions(+), 60 deletions(-) diff --git a/app/src/main/java/org/ethelred/minecraft/webhook/MinecraftServerEvent.java b/app/src/main/java/org/ethelred/minecraft/webhook/MinecraftServerEvent.java index 5a36555..608fac9 100644 --- a/app/src/main/java/org/ethelred/minecraft/webhook/MinecraftServerEvent.java +++ b/app/src/main/java/org/ethelred/minecraft/webhook/MinecraftServerEvent.java @@ -6,7 +6,13 @@ import io.micronaut.core.annotation.Nullable; @Introspected -public class MinecraftServerEvent { +public record MinecraftServerEvent( + @NonNull Type type, + @NonNull String containerId, + @NonNull String containerName, + @NonNull String worldName, + @Nullable String playerName, + @Nullable String playerXuid) { public MinecraftServerEvent( @NonNull Type type, @@ -16,67 +22,10 @@ public MinecraftServerEvent( this(type, containerId, containerName, worldName, null, null); } - public MinecraftServerEvent( - @NonNull Type type, - @NonNull String containerId, - @NonNull String containerName, - @NonNull String worldName, - @Nullable String playerName, - @Nullable String playerXuid) { - this.type = type; - this.containerId = containerId; - this.containerName = containerName; - this.worldName = worldName; - this.playerName = playerName; - this.playerXuid = playerXuid; - } - enum Type { PLAYER_CONNECTED, PLAYER_DISCONNECTED, SERVER_STARTED, SERVER_STOPPED, } - - @NonNull - public Type getType() { - return type; - } - - @NonNull - public String getContainerId() { - return containerId; - } - - @NonNull - public String getContainerName() { - return containerName; - } - - @NonNull - public String getWorldName() { - return worldName; - } - - @Nullable - public String getPlayerName() { - return playerName; - } - - @Nullable - public String getPlayerXuid() { - return playerXuid; - } - - @NonNull private final Type type; - - @NonNull private final String containerId; - - @NonNull private final String containerName; - - @NonNull private final String worldName; - - @Nullable private final String playerName; - - @Nullable private final String playerXuid; } diff --git a/app/src/main/java/org/ethelred/minecraft/webhook/MinecraftServerEventListener.java b/app/src/main/java/org/ethelred/minecraft/webhook/MinecraftServerEventListener.java index 215e803..2af9a0b 100644 --- a/app/src/main/java/org/ethelred/minecraft/webhook/MinecraftServerEventListener.java +++ b/app/src/main/java/org/ethelred/minecraft/webhook/MinecraftServerEventListener.java @@ -38,9 +38,9 @@ public MinecraftServerEventListener( @Async @Override public void onApplicationEvent(MinecraftServerEvent event) { - if (configuration.getEvents().containsKey(event.getType())) { + if (configuration.getEvents().containsKey(event.type())) { var substitutor = new StringSubstitutor(_eventLookup(event), "%", "%", '\\'); - var messageFormat = configuration.getEvents().get(event.getType()); + var messageFormat = configuration.getEvents().get(event.type()); var message = substitutor.replace(messageFormat); sender.sendMessage(event, message); } From 84b7fac1f77f36012ead57c962fd312fbb15dd8f Mon Sep 17 00:00:00 2001 From: Edward Harman Date: Mon, 7 Feb 2022 14:10:41 -0500 Subject: [PATCH 3/3] forgot to upgrade java version in the CI jobs --- .github/workflows/gradle-ci.yml | 4 ++-- .github/workflows/publish.yml | 4 ++-- .github/workflows/publish_mr.yml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/gradle-ci.yml b/.github/workflows/gradle-ci.yml index 93e0f12..199634f 100644 --- a/.github/workflows/gradle-ci.yml +++ b/.github/workflows/gradle-ci.yml @@ -16,10 +16,10 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: ayltai/setup-graalvm@v1 with: - java-version: 11 + java-version: 17 graalvm-version: 21.3.0 - name: Grant execute permission for gradlew run: chmod +x gradlew diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9e1e019..5d092ae 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -25,10 +25,10 @@ jobs: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Set up JDK 11 + - name: Set up JDK 17 uses: ayltai/setup-graalvm@v1 with: - java-version: 11 + java-version: 17 graalvm-version: 21.3.0 - name: Grant execute permission for gradlew run: chmod +x gradlew diff --git a/.github/workflows/publish_mr.yml b/.github/workflows/publish_mr.yml index 91082b2..4eea55e 100644 --- a/.github/workflows/publish_mr.yml +++ b/.github/workflows/publish_mr.yml @@ -26,10 +26,10 @@ jobs: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Set up JDK 11 + - name: Set up JDK 17 uses: ayltai/setup-graalvm@v1 with: - java-version: 11 + java-version: 17 graalvm-version: 21.3.0 - name: Grant execute permission for gradlew run: chmod +x gradlew