1919
2020package org .maxgamer .quickshop .localization .text .distributions .crowdin ;
2121
22- import com .google .common .cache .Cache ;
23- import com .google .common .cache .CacheBuilder ;
2422import com .google .gson .JsonElement ;
2523import com .google .gson .JsonParser ;
2624import com .google .gson .JsonPrimitive ;
2725import lombok .*;
28- import okhttp3 .Request ;
29- import okhttp3 .Response ;
3026import org .apache .commons .codec .digest .DigestUtils ;
3127import org .bukkit .configuration .file .YamlConfiguration ;
3228import org .jetbrains .annotations .NotNull ;
3632import org .maxgamer .quickshop .localization .text .distributions .crowdin .bean .Manifest ;
3733import org .maxgamer .quickshop .util .HttpUtil ;
3834import org .maxgamer .quickshop .util .JsonUtil ;
35+ import org .maxgamer .quickshop .util .MsgUtil ;
3936import org .maxgamer .quickshop .util .Util ;
4037
4138import java .io .File ;
4239import java .io .IOException ;
4340import java .nio .charset .StandardCharsets ;
4441import java .nio .file .Files ;
45- import java .nio .file .StandardOpenOption ;
4642import java .util .*;
47- import java .util .concurrent .TimeUnit ;
48- import java .util .logging .Level ;
4943
5044public 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}
0 commit comments