diff --git a/src/main/java/me/braydon/mc/controller/ServerController.java b/src/main/java/me/braydon/mc/controller/ServerController.java index 7cf0ab5..555bf3d 100644 --- a/src/main/java/me/braydon/mc/controller/ServerController.java +++ b/src/main/java/me/braydon/mc/controller/ServerController.java @@ -3,6 +3,7 @@ package me.braydon.mc.controller; import lombok.NonNull; import lombok.extern.log4j.Log4j2; import me.braydon.mc.model.MinecraftServer; +import me.braydon.mc.model.cache.CachedMinecraftServer; import me.braydon.mc.service.MojangService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; @@ -38,8 +39,8 @@ public final class ServerController { */ @GetMapping("/{platform}/{hostname}") @ResponseBody - public ResponseEntity getServer(@PathVariable @NonNull String platform, - @PathVariable @NonNull String hostname + public ResponseEntity getServer(@PathVariable @NonNull String platform, + @PathVariable @NonNull String hostname ) { return ResponseEntity.ofNullable(mojangService.getMinecraftServer(platform, hostname)); } diff --git a/src/main/java/me/braydon/mc/model/MinecraftServer.java b/src/main/java/me/braydon/mc/model/MinecraftServer.java index 58ab997..31264e5 100644 --- a/src/main/java/me/braydon/mc/model/MinecraftServer.java +++ b/src/main/java/me/braydon/mc/model/MinecraftServer.java @@ -62,9 +62,9 @@ public class MinecraftServer { private final int max; /** - * A sample of players on this server. + * A sample of players on this server, null or empty if no sample. */ - @NonNull private final Sample[] sample; + private final Sample[] sample; /** * A sample player. diff --git a/src/main/java/me/braydon/mc/model/cache/CachedMinecraftServer.java b/src/main/java/me/braydon/mc/model/cache/CachedMinecraftServer.java new file mode 100644 index 0000000..ca72f74 --- /dev/null +++ b/src/main/java/me/braydon/mc/model/cache/CachedMinecraftServer.java @@ -0,0 +1,31 @@ +package me.braydon.mc.model.cache; + +import lombok.*; +import me.braydon.mc.model.MinecraftServer; +import org.springframework.data.annotation.Id; +import org.springframework.data.redis.core.RedisHash; + +import java.io.Serializable; + +/** + * @author Braydon + */ +@AllArgsConstructor @Setter @Getter @ToString +@RedisHash(value = "server", timeToLive = 60L) // 1 minute (in seconds) +public final class CachedMinecraftServer implements Serializable { + /** + * The hostname of the cached server. + */ + @Id @NonNull private transient final String hostname; + + /** + * The cached server. + */ + @NonNull private final MinecraftServer value; + + /** + * The unix timestamp of when this + * server was cached, -1 if not cached. + */ + private long cached; +} \ No newline at end of file diff --git a/src/main/java/me/braydon/mc/model/response/ErrorResponse.java b/src/main/java/me/braydon/mc/model/response/ErrorResponse.java index f22b5e4..89f15a8 100644 --- a/src/main/java/me/braydon/mc/model/response/ErrorResponse.java +++ b/src/main/java/me/braydon/mc/model/response/ErrorResponse.java @@ -1,6 +1,8 @@ package me.braydon.mc.model.response; -import lombok.*; +import lombok.Getter; +import lombok.NonNull; +import lombok.ToString; import org.springframework.http.HttpStatus; import java.util.Date; diff --git a/src/main/java/me/braydon/mc/repository/MinecraftServerCacheRepository.java b/src/main/java/me/braydon/mc/repository/MinecraftServerCacheRepository.java new file mode 100644 index 0000000..bfda340 --- /dev/null +++ b/src/main/java/me/braydon/mc/repository/MinecraftServerCacheRepository.java @@ -0,0 +1,11 @@ +package me.braydon.mc.repository; + +import me.braydon.mc.model.cache.CachedMinecraftServer; +import org.springframework.data.repository.CrudRepository; + +/** + * A cache repository for {@link CachedMinecraftServer}'s. + * + * @author Braydon + */ +public interface MinecraftServerCacheRepository extends CrudRepository { } \ No newline at end of file diff --git a/src/main/java/me/braydon/mc/service/MojangService.java b/src/main/java/me/braydon/mc/service/MojangService.java index bb4255e..b33b162 100644 --- a/src/main/java/me/braydon/mc/service/MojangService.java +++ b/src/main/java/me/braydon/mc/service/MojangService.java @@ -12,10 +12,12 @@ import me.braydon.mc.exception.impl.BadRequestException; import me.braydon.mc.exception.impl.InvalidMinecraftServerPlatform; import me.braydon.mc.exception.impl.ResourceNotFoundException; import me.braydon.mc.model.*; +import me.braydon.mc.model.cache.CachedMinecraftServer; import me.braydon.mc.model.cache.CachedPlayer; import me.braydon.mc.model.cache.CachedPlayerName; import me.braydon.mc.model.token.MojangProfileToken; import me.braydon.mc.model.token.MojangUsernameToUUIDToken; +import me.braydon.mc.repository.MinecraftServerCacheRepository; import me.braydon.mc.repository.PlayerCacheRepository; import me.braydon.mc.repository.PlayerNameCacheRepository; import org.springframework.beans.factory.annotation.Autowired; @@ -49,10 +51,17 @@ public final class MojangService { */ @NonNull private final PlayerCacheRepository playerCache; + /** + * The cache repository for {@link MinecraftServer}'s. + */ + @NonNull private final MinecraftServerCacheRepository minecraftServerCache; + @Autowired - public MojangService(@NonNull PlayerNameCacheRepository playerNameCache, @NonNull PlayerCacheRepository playerCache) { + public MojangService(@NonNull PlayerNameCacheRepository playerNameCache, @NonNull PlayerCacheRepository playerCache, + @NonNull MinecraftServerCacheRepository minecraftServerCache) { this.playerNameCache = playerNameCache; this.playerCache = playerCache; + this.minecraftServerCache = minecraftServerCache; } /** @@ -143,19 +152,37 @@ public final class MojangService { * @throws ResourceNotFoundException if the server isn't found */ @NonNull - public MinecraftServer getMinecraftServer(@NonNull String platformName, @NonNull String hostname) + public CachedMinecraftServer getMinecraftServer(@NonNull String platformName, @NonNull String hostname) throws BadRequestException, InvalidMinecraftServerPlatform, ResourceNotFoundException { MinecraftServer.Platform platform = EnumUtils.getEnumConstant(MinecraftServer.Platform.class, platformName.toUpperCase()); if (platform == null) { // Invalid platform throw new InvalidMinecraftServerPlatform(); } + String lookupHostname = hostname; // The hostname used to lookup the server + + // Check the cache for the server + Optional cached = minecraftServerCache.findById(hostname); + if (cached.isPresent()) { // Respond with the cache if present + log.info("Found server in cache: {}", hostname); + return cached.get(); + } + InetSocketAddress address = DNSUtils.resolveSRV(hostname); // Resolve the SRV record int port = platform.getDefaultPort(); // Port to ping if (address != null) { // SRV was resolved, use the hostname and port hostname = address.getHostName(); port = address.getPort(); } - return platform.getPinger().ping(hostname, port); // Ping the server and return with the response + // Build our server model, cache it, and then return it + CachedMinecraftServer minecraftServer = new CachedMinecraftServer( + lookupHostname, + platform.getPinger().ping(hostname, port), + System.currentTimeMillis() + ); + minecraftServerCache.save(minecraftServer); + log.info("Cached server: {}", hostname); + minecraftServer.setCached(-1L); // Set to -1 to indicate it's not cached in the response + return minecraftServer; } /**