feat(plugins): cache available plugin list
This commit is contained in:
@@ -3,18 +3,22 @@ package jadx.plugins.tools;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import jadx.api.plugins.utils.ZipSecurity;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
import jadx.plugins.tools.data.JadxPluginListCache;
|
||||
import jadx.plugins.tools.data.JadxPluginMetadata;
|
||||
import jadx.plugins.tools.resolvers.github.GithubTools;
|
||||
import jadx.plugins.tools.resolvers.github.LocationInfo;
|
||||
@@ -22,6 +26,8 @@ import jadx.plugins.tools.resolvers.github.data.Asset;
|
||||
import jadx.plugins.tools.resolvers.github.data.Release;
|
||||
import jadx.plugins.tools.utils.PluginUtils;
|
||||
|
||||
import static jadx.plugins.tools.utils.PluginFiles.PLUGINS_LIST_CACHE;
|
||||
|
||||
/**
|
||||
* TODO: implement list caching (on disk) with check for new release
|
||||
*/
|
||||
@@ -31,32 +37,96 @@ public class JadxPluginsList {
|
||||
private static final Type LIST_TYPE = new TypeToken<List<JadxPluginMetadata>>() {
|
||||
}.getType();
|
||||
|
||||
private static final Type CACHE_TYPE = new TypeToken<JadxPluginListCache>() {
|
||||
}.getType();
|
||||
|
||||
public static JadxPluginsList getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private @Nullable List<JadxPluginMetadata> cache;
|
||||
private @Nullable JadxPluginListCache loadedList;
|
||||
|
||||
private JadxPluginsList() {
|
||||
}
|
||||
|
||||
public synchronized List<JadxPluginMetadata> fetch() {
|
||||
if (cache != null) {
|
||||
return cache;
|
||||
/**
|
||||
* List provider with update callback.
|
||||
* Can be called one or two times:
|
||||
* <br>
|
||||
* - Apply cached data first
|
||||
* <br>
|
||||
* - If update is available, apply data after fetch
|
||||
* <br>
|
||||
* Method call is blocking.
|
||||
*/
|
||||
public synchronized void get(Consumer<List<JadxPluginMetadata>> consumer) {
|
||||
if (loadedList != null) {
|
||||
consumer.accept(loadedList.getList());
|
||||
return;
|
||||
}
|
||||
JadxPluginListCache listCache = loadCache();
|
||||
if (listCache != null) {
|
||||
consumer.accept(listCache.getList());
|
||||
}
|
||||
Release release = fetchLatestRelease();
|
||||
if (listCache == null || !listCache.getVersion().equals(release.getName())) {
|
||||
JadxPluginListCache updatedList = fetchBundle(release);
|
||||
saveCache(updatedList);
|
||||
consumer.accept(updatedList.getList());
|
||||
}
|
||||
}
|
||||
|
||||
public List<JadxPluginMetadata> get() {
|
||||
AtomicReference<List<JadxPluginMetadata>> holder = new AtomicReference<>();
|
||||
get(holder::set);
|
||||
return holder.get();
|
||||
}
|
||||
|
||||
private @Nullable JadxPluginListCache loadCache() {
|
||||
if (!Files.isRegularFile(PLUGINS_LIST_CACHE)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
String jsonStr = FileUtils.readFile(PLUGINS_LIST_CACHE);
|
||||
return buildGson().fromJson(jsonStr, CACHE_TYPE);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void saveCache(JadxPluginListCache listCache) {
|
||||
try {
|
||||
String jsonStr = buildGson().toJson(listCache, CACHE_TYPE);
|
||||
FileUtils.writeFile(PLUGINS_LIST_CACHE, jsonStr);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error saving file: " + PLUGINS_LIST_CACHE, e);
|
||||
}
|
||||
loadedList = listCache;
|
||||
}
|
||||
|
||||
private static Gson buildGson() {
|
||||
return new GsonBuilder().setPrettyPrinting().create();
|
||||
}
|
||||
|
||||
private Release fetchLatestRelease() {
|
||||
LocationInfo latest = new LocationInfo("jadx-decompiler", "jadx-plugins-list", "list", null);
|
||||
Release release = GithubTools.fetchRelease(latest);
|
||||
List<Asset> assets = release.getAssets();
|
||||
if (assets.isEmpty()) {
|
||||
throw new RuntimeException("Release don't have assets");
|
||||
}
|
||||
Asset listAsset = assets.get(0);
|
||||
return release;
|
||||
}
|
||||
|
||||
private JadxPluginListCache fetchBundle(Release release) {
|
||||
Asset listAsset = release.getAssets().get(0);
|
||||
Path tmpListFile = FileUtils.createTempFile("list.zip");
|
||||
PluginUtils.downloadFile(listAsset.getDownloadUrl(), tmpListFile);
|
||||
|
||||
List<JadxPluginMetadata> entries = loadListBundle(tmpListFile);
|
||||
cache = entries;
|
||||
return entries;
|
||||
JadxPluginListCache listCache = new JadxPluginListCache();
|
||||
listCache.setVersion(release.getName());
|
||||
listCache.setList(loadListBundle(tmpListFile));
|
||||
return listCache;
|
||||
}
|
||||
|
||||
private static List<JadxPluginMetadata> loadListBundle(Path tmpListFile) {
|
||||
@@ -73,14 +143,4 @@ public class JadxPluginsList {
|
||||
});
|
||||
return entries;
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public synchronized List<JadxPluginMetadata> fetchFromLocalBundle(Path bundleFile) {
|
||||
if (cache != null) {
|
||||
return cache;
|
||||
}
|
||||
List<JadxPluginMetadata> entries = loadListBundle(bundleFile);
|
||||
cache = entries;
|
||||
return entries;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package jadx.plugins.tools.data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class JadxPluginListCache {
|
||||
private String version;
|
||||
private List<JadxPluginMetadata> list;
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public List<JadxPluginMetadata> getList() {
|
||||
return list;
|
||||
}
|
||||
|
||||
public void setList(List<JadxPluginMetadata> list) {
|
||||
this.list = list;
|
||||
}
|
||||
}
|
||||
@@ -10,12 +10,13 @@ import static jadx.core.utils.files.FileUtils.makeDirs;
|
||||
public class PluginFiles {
|
||||
private static final ProjectDirectories DIRS = ProjectDirectories.from("io.github", "skylot", "jadx");
|
||||
|
||||
public static final Path PLUGINS_DIR = Paths.get(DIRS.configDir, "plugins");
|
||||
private static final Path PLUGINS_DIR = Paths.get(DIRS.configDir, "plugins");
|
||||
public static final Path PLUGINS_JSON = PLUGINS_DIR.resolve("plugins.json");
|
||||
public static final Path INSTALLED_DIR = PLUGINS_DIR.resolve("installed");
|
||||
public static final Path DROPINS_DIR = PLUGINS_DIR.resolve("dropins");
|
||||
|
||||
public static final Path CACHE_DIR = Paths.get(DIRS.cacheDir);
|
||||
private static final Path CACHE_DIR = Paths.get(DIRS.cacheDir);
|
||||
public static final Path PLUGINS_LIST_CACHE = CACHE_DIR.resolve("plugin-list.json");
|
||||
|
||||
static {
|
||||
makeDirs(INSTALLED_DIR);
|
||||
|
||||
Reference in New Issue
Block a user