Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement http caching #2840

Merged
merged 6 commits into from
Jan 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@
*/
package com.djrapitops.plan.delivery.web.resource;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
import java.util.function.Supplier;

/**
* Represents a customizable resource.
Expand Down Expand Up @@ -61,19 +60,55 @@ static WebResource create(String utf8String) {
* @throws IOException If the stream can not be read.
*/
static WebResource create(InputStream in) throws IOException {
return create(in, null);
}

/**
* Creates a new WebResource from an InputStream.
*
* @param in InputStream for the resource, closed after inside the method.
* @param lastModified Epoch millisecond the resource was last modified
* @return WebResource.
* @throws IOException If the stream can not be read.
*/
static WebResource create(InputStream in, Long lastModified) throws IOException {
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
int read;
byte[] bytes = new byte[1024];
while ((read = in.read(bytes)) != -1) {
out.write(bytes, 0, read);
}

return new ByteResource(out.toByteArray());
return new ByteResource(out.toByteArray(), lastModified);
} finally {
in.close();
}
}

/**
* Create a lazy WebResource that only reads contents if necessary.
*
* @param in Supplier for InputStream, a lazy method that reads input when necessary.
* @param lastModified Last modified date for the resource.
* @return WebResource.
*/
static WebResource create(Supplier<InputStream> in, Long lastModified) {
return new LazyWebResource(in, () -> {
try (ByteArrayOutputStream out = new ByteArrayOutputStream();
InputStream input = in.get()) {
int read;
byte[] bytes = new byte[1024];
while ((read = input.read(bytes)) != -1) {
out.write(bytes, 0, read);
}

return out.toByteArray();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}, lastModified);
}

byte[] asBytes();

/**
Expand All @@ -85,11 +120,21 @@ static WebResource create(InputStream in) throws IOException {

InputStream asStream();

default Optional<Long> getLastModified() {
return Optional.empty();
}

final class ByteResource implements WebResource {
private final byte[] content;
private final Long lastModified;

public ByteResource(byte[] content) {
this(content, null);
}

public ByteResource(byte[] content, Long lastModified) {
this.content = content;
this.lastModified = lastModified;
}

@Override
Expand All @@ -106,5 +151,42 @@ public String asString() {
public InputStream asStream() {
return new ByteArrayInputStream(content);
}

@Override
public Optional<Long> getLastModified() {
return Optional.ofNullable(lastModified);
}
}

final class LazyWebResource implements WebResource {
private final Supplier<InputStream> inputStreamSupplier;
private final Supplier<byte[]> contentSupplier;
private final Long lastModified;

public LazyWebResource(Supplier<InputStream> inputStreamSupplier, Supplier<byte[]> contentSupplier, Long lastModified) {
this.inputStreamSupplier = inputStreamSupplier;
this.contentSupplier = contentSupplier;
this.lastModified = lastModified;
}

@Override
public byte[] asBytes() {
return contentSupplier.get();
}

@Override
public String asString() {
return new String(asBytes(), StandardCharsets.UTF_8);
}

@Override
public InputStream asStream() {
return inputStreamSupplier.get();
}

@Override
public Optional<Long> getLastModified() {
return Optional.ofNullable(lastModified);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ public String onRequest(OfflinePlayer player, @Untrusted String params) {
if ("Server thread".equalsIgnoreCase(Thread.currentThread().getName())) {
return getCached(params, uuid);
}
return getPlaceholderValue(params, uuid);

return Optional.ofNullable(getCached(params, uuid))
.orElseGet(() -> getPlaceholderValue(params, uuid));
} catch (IllegalStateException e) {
if ("zip file closed".equals(e.getMessage())) {
return null; // Plan is disabled.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import com.djrapitops.plan.storage.database.Database;
import com.djrapitops.plan.storage.database.queries.containers.PlayerContainerQuery;
import com.djrapitops.plan.storage.database.queries.objects.ServerQueries;
import com.djrapitops.plan.storage.database.queries.objects.SessionQueries;
import com.djrapitops.plan.utilities.comparators.DateHolderRecentComparator;
import com.djrapitops.plan.utilities.java.Lists;
import com.djrapitops.plan.utilities.java.Maps;
Expand Down Expand Up @@ -91,6 +92,10 @@ public PlayerJSONCreator(
this.graphs = graphs;
}

public long getLastSeen(UUID playerUUID) {
return dbSystem.getDatabase().query(SessionQueries.lastSeen(playerUUID));
}

public Map<String, Object> createJSONAsMap(UUID playerUUID) {
Database db = dbSystem.getDatabase();

Expand Down Expand Up @@ -226,6 +231,7 @@ private Map<String, Object> createInfoJSONMap(PlayerContainer player, Map<Server
info.put("best_ping", bestPing != -1.0 ? bestPing + " ms" : unavailable);
info.put("registered", player.getValue(PlayerKeys.REGISTERED).map(year).orElse("-"));
info.put("last_seen", player.getValue(PlayerKeys.LAST_SEEN).map(year).orElse("-"));
info.put("last_seen_raw_value", player.getValue(PlayerKeys.LAST_SEEN).orElse(0L));

return info;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.djrapitops.plan.delivery.rendering.pages;

import com.djrapitops.plan.delivery.formatting.PlaceholderReplacer;
import com.djrapitops.plan.delivery.web.resource.WebResource;
import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.settings.locale.Locale;
import com.djrapitops.plan.settings.theme.Theme;
Expand All @@ -30,15 +31,15 @@
*/
public class LoginPage implements Page {

private final String template;
private final WebResource template;
private final ServerInfo serverInfo;
private final Locale locale;
private final Theme theme;

private final VersionChecker versionChecker;

LoginPage(
String htmlTemplate,
WebResource htmlTemplate,
ServerInfo serverInfo,
Locale locale,
Theme theme,
Expand All @@ -51,12 +52,17 @@ public class LoginPage implements Page {
this.versionChecker = versionChecker;
}

@Override
public long lastModified() {
return template.getLastModified().orElseGet(System::currentTimeMillis);
}

@Override
public String toHtml() {
PlaceholderReplacer placeholders = new PlaceholderReplacer();
placeholders.put("command", getCommand());
placeholders.put("version", versionChecker.getCurrentVersion());
return UnaryChain.of(template)
return UnaryChain.of(template.asString())
.chain(theme::replaceThemeColors)
.chain(placeholders::apply)
.chain(locale::replaceLanguageInHtml)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,8 @@
*/
public interface Page {
String toHtml();

default long lastModified() {
return System.currentTimeMillis();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.djrapitops.plan.delivery.rendering.html.icon.Icon;
import com.djrapitops.plan.delivery.web.ResourceService;
import com.djrapitops.plan.delivery.web.resolver.exception.NotFoundException;
import com.djrapitops.plan.delivery.web.resource.WebResource;
import com.djrapitops.plan.delivery.webserver.Addresses;
import com.djrapitops.plan.delivery.webserver.cache.JSONStorage;
import com.djrapitops.plan.extension.implementation.results.ExtensionData;
Expand All @@ -41,7 +42,6 @@
import com.djrapitops.plan.utilities.dev.Untrusted;
import com.djrapitops.plan.version.VersionChecker;
import dagger.Lazy;
import org.apache.commons.lang3.StringUtils;

import javax.inject.Inject;
import javax.inject.Singleton;
Expand Down Expand Up @@ -101,15 +101,12 @@ public Page playersPage() throws IOException {
return reactPage();
}

return new PlayersPage(getResource("players.html"), versionChecker.get(),
return new PlayersPage(getResourceAsString("players.html"), versionChecker.get(),
config.get(), theme.get(), serverInfo.get());
}

public Page reactPage() throws IOException {
String reactHtml = StringUtils.replace(
getResource("index.html"),
"/static", getBasePath() + "/static");
return () -> reactHtml;
return new ReactPage(getBasePath(), getResource("index.html"));
}

private String getBasePath() {
Expand All @@ -135,7 +132,7 @@ public Page serverPage(ServerUUID serverUUID) throws IOException {
}

return new ServerPage(
getResource("server.html"),
getResourceAsString("server.html"),
server,
config.get(),
theme.get(),
Expand All @@ -158,7 +155,7 @@ public Page playerPage(UUID playerUUID) throws IOException {
}

return new PlayerPage(
getResource("player.html"), player,
getResourceAsString("player.html"), player,
versionChecker.get(),
config.get(),
this,
Expand Down Expand Up @@ -207,7 +204,7 @@ public Page networkPage() throws IOException {
return reactPage();
}

return new NetworkPage(getResource("network.html"),
return new NetworkPage(getResourceAsString("network.html"),
dbSystem.get(),
versionChecker.get(),
config.get(),
Expand All @@ -223,7 +220,7 @@ public Page networkPage() throws IOException {
public Page internalErrorPage(String message, @Untrusted Throwable error) {
try {
return new InternalErrorPage(
getResource("error.html"), message, error,
getResourceAsString("error.html"), message, error,
versionChecker.get());
} catch (IOException noParse) {
return () -> "Error occurred: " + error.toString() +
Expand All @@ -234,20 +231,24 @@ public Page internalErrorPage(String message, @Untrusted Throwable error) {

public Page errorPage(String title, String error) throws IOException {
return new ErrorMessagePage(
getResource("error.html"), title, error,
getResourceAsString("error.html"), title, error,
versionChecker.get(), theme.get());
}

public Page errorPage(Icon icon, String title, String error) throws IOException {
return new ErrorMessagePage(
getResource("error.html"), icon, title, error, theme.get(), versionChecker.get());
getResourceAsString("error.html"), icon, title, error, theme.get(), versionChecker.get());
}

public String getResource(String name) throws IOException {
public String getResourceAsString(String name) throws IOException {
return getResource(name).asString();
}

public WebResource getResource(String name) throws IOException {
try {
return ResourceService.getInstance().getResource("Plan", name,
() -> files.get().getResourceFromJar("web/" + name).asWebResource()
).asString();
);
} catch (UncheckedIOException readFail) {
throw readFail.getCause();
}
Expand All @@ -274,7 +275,7 @@ public Page queryPage() throws IOException {
return reactPage();
}
return new QueryPage(
getResource("query.html"),
getResourceAsString("query.html"),
locale.get(), theme.get(), versionChecker.get()
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.delivery.rendering.pages;

import com.djrapitops.plan.delivery.web.resource.WebResource;
import org.apache.commons.lang3.StringUtils;

/**
* Represents React index.html.
*
* @author AuroraLS3
*/
public class ReactPage implements Page {

private final String basePath;
private final WebResource reactHtml;

public ReactPage(String basePath, WebResource reactHtml) {
this.basePath = basePath;
this.reactHtml = reactHtml;
}

@Override
public String toHtml() {
return StringUtils.replace(
reactHtml.asString(),
"/static", basePath + "/static");
}

@Override
public long lastModified() {
return reactHtml.getLastModified().orElseGet(System::currentTimeMillis);
}
}
Loading