Skip to content

Commit af604ac

Browse files
committed
try refactor crowdin ota system
1 parent 0c18c58 commit af604ac

File tree

2 files changed

+140
-73
lines changed

2 files changed

+140
-73
lines changed

src/main/java/org/maxgamer/quickshop/localization/text/distributions/crowdin/CrowdinOTA.java

Lines changed: 120 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,10 @@
1919

2020
package org.maxgamer.quickshop.localization.text.distributions.crowdin;
2121

22-
import com.google.common.cache.Cache;
23-
import com.google.common.cache.CacheBuilder;
2422
import com.google.gson.JsonElement;
2523
import com.google.gson.JsonParser;
2624
import com.google.gson.JsonPrimitive;
2725
import lombok.*;
28-
import okhttp3.Request;
29-
import okhttp3.Response;
3026
import org.apache.commons.codec.digest.DigestUtils;
3127
import org.bukkit.configuration.file.YamlConfiguration;
3228
import org.jetbrains.annotations.NotNull;
@@ -36,22 +32,17 @@
3632
import org.maxgamer.quickshop.localization.text.distributions.crowdin.bean.Manifest;
3733
import org.maxgamer.quickshop.util.HttpUtil;
3834
import org.maxgamer.quickshop.util.JsonUtil;
35+
import org.maxgamer.quickshop.util.MsgUtil;
3936
import org.maxgamer.quickshop.util.Util;
4037

4138
import java.io.File;
4239
import java.io.IOException;
4340
import java.nio.charset.StandardCharsets;
4441
import java.nio.file.Files;
45-
import java.nio.file.StandardOpenOption;
4642
import java.util.*;
47-
import java.util.concurrent.TimeUnit;
48-
import java.util.logging.Level;
4943

5044
public class CrowdinOTA implements Distribution {
5145
protected static final String CROWDIN_OTA_HOST = "https://distributions.crowdin.net/daf1a8db40f132ce157c457xrm4/";
52-
protected final Cache<String, String> requestCachePool = CacheBuilder.newBuilder()
53-
.expireAfterWrite(7, TimeUnit.DAYS)
54-
.build();
5546
private final QuickShop plugin;
5647

5748
public CrowdinOTA(QuickShop plugin) {
@@ -79,27 +70,7 @@ public Manifest getManifest() {
7970
@Nullable
8071
public String getManifestJson() {
8172
String url = CROWDIN_OTA_HOST + "manifest.json";
82-
String data;
83-
if (requestCachePool.getIfPresent(url) != null) {
84-
return requestCachePool.getIfPresent(url);
85-
}
86-
try (Response response = HttpUtil.create().getClient().newCall(new Request.Builder().get().url(url).build()).execute()) {
87-
val body = response.body();
88-
if (body == null) {
89-
return null;
90-
}
91-
data = body.string();
92-
if (response.code() != 200) {
93-
plugin.getLogger().warning("Couldn't get manifest: " + response.code() + ", please report to QuickShop!");
94-
return null;
95-
}
96-
requestCachePool.put(url, data);
97-
} catch (IOException e) {
98-
e.printStackTrace();
99-
plugin.getLogger().log(Level.WARNING, "Failed to download manifest.json, multi-language system won't work");
100-
return null;
101-
}
102-
return data;
73+
return HttpUtil.createGet(url);
10374
}
10475

10576
/**
@@ -178,58 +149,84 @@ public String getFile(String fileCrowdinPath, String crowdinLocale, boolean forc
178149
}
179150
// Post path (replaced with locale code)
180151
String postProcessingPath = fileCrowdinPath.replace("%locale%", crowdinLocale);
181-
// Create path hash to store the file
182-
String pathHash = DigestUtils.sha1Hex(postProcessingPath);
183-
// Reading metadata
184-
File metadataFile = new File(Util.getCacheFolder(), "i18n.metadata");
185-
YamlConfiguration cacheMetadata = YamlConfiguration.loadConfiguration(metadataFile);
186-
// Reading cloud timestamp
187-
long localeTimestamp = cacheMetadata.getLong(pathHash + ".timestamp");
188-
// Reading locale cache
189-
File cachedDataFile = new File(Util.getCacheFolder(), pathHash);
190-
String data = null;
191-
// Getting local cache
192-
if (cachedDataFile.exists()) {
193-
Util.debugLog("Reading data from local cache: " + cachedDataFile.getCanonicalPath());
194-
data = Util.readToString(cachedDataFile);
195-
}
196-
// invalidate cache, flush it
197-
// force flush required OR local cache not exists OR outdated
198-
if (forceFlush || data == null || localeTimestamp != manifest.getTimestamp()) {
199-
String url = CROWDIN_OTA_HOST + "content" + fileCrowdinPath.replace("%locale%", crowdinLocale);
200-
//Util.debugLog("Reading data from remote server: " + url);
201-
plugin.getLogger().info("Downloading translation " + crowdinLocale + " from: " + url);
202-
try (Response response = HttpUtil.create().getClient().newCall(new Request.Builder().get().url(url).build()).execute()) {
203-
val body = response.body();
204-
if (body == null) {
205-
throw new OTAException(response.code(), ""); // Returns empty string (failed to getting content)
152+
OTACacheControl otaCacheControl = new OTACacheControl();
153+
// Validating the manifest
154+
Long manifestTimestamp = getManifest().getTimestamp();
155+
if (Long.valueOf(otaCacheControl.readManifestTimestamp()).equals(getManifest().getTimestamp()) && !forceFlush) {
156+
// Use cache
157+
try {
158+
// Check cache outdated
159+
if (!otaCacheControl.isCachedObjectOutdated(postProcessingPath, manifestTimestamp)) {
160+
// Return the caches
161+
return new String(otaCacheControl.readObjectCache(postProcessingPath), StandardCharsets.UTF_8);
206162
}
207-
data = body.string();
208-
if (response.code() != 200) {
209-
throw new OTAException(response.code(), data);
210-
}
211-
// save to local cache file
212-
Files.write(cachedDataFile.toPath(), data.getBytes(StandardCharsets.UTF_8));
213-
} catch (IOException e) {
214-
plugin.getLogger().log(Level.WARNING, "Failed to download manifest.json, multi-language system may won't work");
215-
e.printStackTrace();
216-
return "";
163+
} catch (Exception exception) {
164+
MsgUtil.debugStackTrace(exception.getStackTrace());
217165
}
218-
// update cache index
219-
cacheMetadata.set(pathHash + ".timestamp", manifest.getTimestamp());
220-
cacheMetadata.save(metadataFile);
221-
return data;
222166
}
167+
// Out of the cache
168+
if (manifestTimestamp == null) { // null-safe
169+
manifestTimestamp = System.currentTimeMillis();
170+
}
171+
String url = CROWDIN_OTA_HOST + "content" + fileCrowdinPath.replace("%locale%", crowdinLocale);
172+
plugin.getLogger().info("Updating translation " + crowdinLocale + " from: " + url);
173+
String data = HttpUtil.createGet(url);
174+
if (data == null) {
175+
// Failed to grab data
176+
throw new OTAException();
177+
}
178+
// Successfully grab the data from the remote server
179+
otaCacheControl.writeObjectCache(postProcessingPath, data.getBytes(StandardCharsets.UTF_8), manifestTimestamp);
223180
return data;
181+
182+
// String pathHash = DigestUtils.sha1Hex(postProcessingPath);
183+
// // Reading metadata
184+
// File metadataFile = new File(Util.getCacheFolder(), "i18n.metadata");
185+
// YamlConfiguration cacheMetadata = YamlConfiguration.loadConfiguration(metadataFile);
186+
// // Reading cloud timestamp
187+
// long localeTimestamp = cacheMetadata.getLong(pathHash + ".timestamp");
188+
// // Reading locale cache
189+
// File cachedDataFile = new File(Util.getCacheFolder(), pathHash);
190+
// String data = null;
191+
// // Getting local cache
192+
// if (cachedDataFile.exists()) {
193+
// Util.debugLog("Reading data from local cache: " + cachedDataFile.getCanonicalPath());
194+
// data = Util.readToString(cachedDataFile);
195+
// }
196+
// // invalidate cache, flush it
197+
// // force flush required OR local cache not exists OR outdated
198+
// if (forceFlush || data == null || localeTimestamp != manifest.getTimestamp()) {
199+
// String url = CROWDIN_OTA_HOST + "content" + fileCrowdinPath.replace("%locale%", crowdinLocale);
200+
// //Util.debugLog("Reading data from remote server: " + url);
201+
// plugin.getLogger().info("Downloading translation " + crowdinLocale + " from: " + url);
202+
// try (Response response = HttpUtil.create().getClient().newCall(new Request.Builder().get().url(url).build()).execute()) {
203+
// val body = response.body();
204+
// if (body == null) {
205+
// throw new OTAException(response.code(), ""); // Returns empty string (failed to getting content)
206+
// }
207+
// data = body.string();
208+
// if (response.code() != 200) {
209+
// throw new OTAException(response.code(), data);
210+
// }
211+
// // save to local cache file
212+
// Files.write(cachedDataFile.toPath(), data.getBytes(StandardCharsets.UTF_8));
213+
// } catch (IOException e) {
214+
// plugin.getLogger().log(Level.WARNING, "Failed to download manifest.json, multi-language system may won't work");
215+
// e.printStackTrace();
216+
// return "";
217+
// }
218+
// // update cache index
219+
// cacheMetadata.set(pathHash + ".timestamp", manifest.getTimestamp());
220+
// cacheMetadata.save(metadataFile);
221+
// return data;
222+
// }
224223
}
225224

226225
@EqualsAndHashCode(callSuper = true)
227226
@AllArgsConstructor
228227
@Builder
229228
@Data
230229
public static class OTAException extends Exception {
231-
private int httpCode;
232-
private String content;
233230
}
234231

235232
@AllArgsConstructor
@@ -240,4 +237,55 @@ public static class CrowdinGetFileRequest {
240237
private String crowdinLocale;
241238
private boolean forceFlush;
242239
}
240+
241+
public static class OTACacheControl {
242+
private final File metadataFile = new File(Util.getCacheFolder(), "i18n.metadata");
243+
private final YamlConfiguration metadata;
244+
245+
public OTACacheControl() {
246+
this.metadata = YamlConfiguration.loadConfiguration(this.metadataFile);
247+
}
248+
249+
@SneakyThrows
250+
private void save() {
251+
this.metadata.save(this.metadataFile);
252+
}
253+
254+
private String hash(String str) {
255+
return DigestUtils.sha1Hex(str);
256+
}
257+
258+
public long readManifestTimestamp() {
259+
return this.metadata.getLong("manifest.timestamp", 0);
260+
}
261+
262+
public void writeManifestTimestamp(long timestamp) {
263+
this.metadata.set("manigest.timestamp", timestamp);
264+
save();
265+
}
266+
267+
public long readCachedObjectTimestamp(String path) {
268+
String cacheKey = hash(path);
269+
return this.metadata.getLong("objects." + cacheKey + ".timestamp");
270+
}
271+
272+
public boolean isCachedObjectOutdated(String path, long manifestTimestamp) {
273+
String cacheKey = hash(path);
274+
return this.metadata.getLong("objects." + cacheKey + ".timestamp") != manifestTimestamp;
275+
}
276+
277+
public byte[] readObjectCache(String path) throws IOException {
278+
String cacheKey = hash(path);
279+
return Files.readAllBytes(new File(Util.getCacheFolder(), cacheKey).toPath());
280+
}
281+
282+
public void writeObjectCache(String path, byte[] data, long manifestTimestamp) throws IOException {
283+
String cacheKey = hash(path);
284+
Files.write(new File(Util.getCacheFolder(), cacheKey).toPath(), data);
285+
this.metadata.set("objects." + cacheKey, manifestTimestamp);
286+
save();
287+
}
288+
289+
290+
}
243291
}

src/main/java/org/maxgamer/quickshop/localization/text/distributions/crowdin/bean/Manifest.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
1+
/*
2+
* This file is a part of project QuickShop, the name is Manifest.java
3+
* Copyright (C) PotatoCraft Studio and contributors
4+
*
5+
* This program is free software: you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License as published by the
7+
* Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful, but WITHOUT
11+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13+
* for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*
18+
*/
19+
120
package org.maxgamer.quickshop.localization.text.distributions.crowdin.bean;
221

322
import com.fasterxml.jackson.annotation.JsonProperty;
@@ -18,6 +37,6 @@ public class Manifest {
1837
@JsonProperty("custom_languages")
1938
private List<?> customLanguages;
2039
@JsonProperty("timestamp")
21-
private Integer timestamp;
40+
private Long timestamp;
2241

2342
}

0 commit comments

Comments
 (0)