mirror of
https://github.com/PlaceholderAPI/PlaceholderAPI
synced 2024-11-18 00:46:55 +01:00
rewrote discovery and registration code to be composable and higher level
This commit is contained in:
parent
ee33de5ec8
commit
8360511c50
@ -5,16 +5,16 @@ import me.clip.placeholderapi.PlaceholderAPIPlugin;
|
||||
import me.clip.placeholderapi.commands.PlaceholderCommand;
|
||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||
import me.clip.placeholderapi.expansion.cloud.CloudExpansion;
|
||||
import me.clip.placeholderapi.util.Futures;
|
||||
import me.clip.placeholderapi.util.Msg;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
@ -67,9 +67,8 @@ public final class CommandECloudUpdate extends PlaceholderCommand
|
||||
Msg.msg(sender,
|
||||
"&aUpdating expansions: " + expansions.stream().map(CloudExpansion::getName).collect(Collectors.joining("&7, &6", "&8[&6", "&8]&r")));
|
||||
|
||||
downloadExpansions(plugin, expansions)
|
||||
.thenCompose(files -> discoverExpansions(plugin, files))
|
||||
.whenComplete((classes, exception) -> {
|
||||
|
||||
Futures.onMainThread(plugin, downloadAndDiscover(expansions, plugin), (classes, exception) -> {
|
||||
if (exception != null)
|
||||
{
|
||||
Msg.msg(sender,
|
||||
@ -80,17 +79,18 @@ public final class CommandECloudUpdate extends PlaceholderCommand
|
||||
Msg.msg(sender,
|
||||
"&aSuccessfully downloaded updates, registering new versions.");
|
||||
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
|
||||
final String message = classes.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(plugin.getLocalExpansionManager()::register)
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.map(expansion -> " &a" + expansion.getName() + " &f" + expansion.getVersion())
|
||||
.collect(Collectors.joining("\n"));
|
||||
|
||||
Msg.msg(sender,
|
||||
"&7Registered expansions:",
|
||||
message);
|
||||
});
|
||||
"&7Registered expansions:", message);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@ -114,22 +114,12 @@ public final class CommandECloudUpdate extends PlaceholderCommand
|
||||
}
|
||||
|
||||
|
||||
public static CompletableFuture<List<File>> downloadExpansions(@NotNull final PlaceholderAPIPlugin plugin, @NotNull final List<CloudExpansion> expansions)
|
||||
private static CompletableFuture<List<@Nullable Class<? extends PlaceholderExpansion>>> downloadAndDiscover(@NotNull final List<CloudExpansion> expansions, @NotNull final PlaceholderAPIPlugin plugin)
|
||||
{
|
||||
final List<CompletableFuture<File>> futures = expansions.stream()
|
||||
return expansions.stream()
|
||||
.map(expansion -> plugin.getCloudExpansionManager().downloadExpansion(expansion, expansion.getVersion()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenApplyAsync(v -> futures.stream().map(CompletableFuture::join).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
public static CompletableFuture<List<Class<? extends PlaceholderExpansion>>> discoverExpansions(@NotNull final PlaceholderAPIPlugin plugin, @NotNull final List<File> files)
|
||||
{
|
||||
final List<CompletableFuture<List<Class<? extends PlaceholderExpansion>>>> futures = files.stream()
|
||||
.map(file -> plugin.getLocalExpansionManager().findExpansionsInFile(file))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenApplyAsync(v -> futures.stream().map(CompletableFuture::join).flatMap(Collection::stream).collect(Collectors.toList()));
|
||||
.map(future -> future.thenCompose(plugin.getLocalExpansionManager()::findExpansionInFile))
|
||||
.collect(Futures.collector());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,8 +4,8 @@ import me.clip.placeholderapi.PlaceholderAPIPlugin;
|
||||
import me.clip.placeholderapi.commands.PlaceholderCommand;
|
||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||
import me.clip.placeholderapi.expansion.manager.LocalExpansionManager;
|
||||
import me.clip.placeholderapi.util.Futures;
|
||||
import me.clip.placeholderapi.util.Msg;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
@ -45,7 +45,7 @@ public final class CommandExpansionRegister extends PlaceholderCommand
|
||||
return;
|
||||
}
|
||||
|
||||
manager.findExpansionsInFile(file).whenComplete((classes, exception) -> {
|
||||
Futures.onMainThread(plugin, manager.findExpansionInFile(file), (clazz, exception) -> {
|
||||
if (exception != null)
|
||||
{
|
||||
Msg.msg(sender,
|
||||
@ -55,15 +55,15 @@ public final class CommandExpansionRegister extends PlaceholderCommand
|
||||
return;
|
||||
}
|
||||
|
||||
if (classes.isEmpty())
|
||||
if (clazz == null)
|
||||
{
|
||||
Msg.msg(sender,
|
||||
"&cNo expansion class found in file: &f" + file);
|
||||
return;
|
||||
}
|
||||
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
final Optional<PlaceholderExpansion> expansion = manager.register(classes.get(0));
|
||||
|
||||
final Optional<PlaceholderExpansion> expansion = manager.register(clazz);
|
||||
if (!expansion.isPresent())
|
||||
{
|
||||
Msg.msg(sender,
|
||||
@ -73,7 +73,7 @@ public final class CommandExpansionRegister extends PlaceholderCommand
|
||||
|
||||
Msg.msg(sender,
|
||||
"&aSuccessfully registered expansion: &f" + expansion.get().getName());
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ import me.clip.placeholderapi.expansion.Taskable;
|
||||
import me.clip.placeholderapi.expansion.VersionSpecific;
|
||||
import me.clip.placeholderapi.expansion.cloud.CloudExpansion;
|
||||
import me.clip.placeholderapi.util.FileUtil;
|
||||
import me.clip.placeholderapi.util.Futures;
|
||||
import me.clip.placeholderapi.util.Msg;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
@ -28,7 +29,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@ -290,19 +291,18 @@ public final class LocalExpansionManager implements Listener
|
||||
{
|
||||
plugin.getLogger().info("Placeholder expansion registration initializing...");
|
||||
|
||||
findExpansionsOnDisk().whenCompleteAsync((classes, exception) -> {
|
||||
Futures.onMainThread(plugin, findExpansionsOnDisk(), (classes, exception) -> {
|
||||
if (exception != null)
|
||||
{
|
||||
plugin.getLogger().log(Level.SEVERE, "failed to load class files of expansions", exception);
|
||||
return;
|
||||
}
|
||||
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
final long registered = classes.stream().map(this::register).filter(Optional::isPresent).count();
|
||||
|
||||
Msg.msg(sender,
|
||||
registered == 0 ? "&6No expansions were registered!" : registered + "&a placeholder hooks successfully registered!");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void unregisterAll()
|
||||
@ -320,33 +320,26 @@ public final class LocalExpansionManager implements Listener
|
||||
|
||||
|
||||
@NotNull
|
||||
public CompletableFuture<List<Class<? extends PlaceholderExpansion>>> findExpansionsOnDisk()
|
||||
public CompletableFuture<@NotNull List<@NotNull Class<? extends PlaceholderExpansion>>> findExpansionsOnDisk()
|
||||
{
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try
|
||||
{
|
||||
return FileUtil.getClasses(getExpansionsFolder(), PlaceholderExpansion.class);
|
||||
}
|
||||
catch (final IOException | ClassNotFoundException ex)
|
||||
{
|
||||
throw new CompletionException(ex);
|
||||
}
|
||||
});
|
||||
return Arrays.stream(folder.listFiles((dir, name) -> name.endsWith(".jar")))
|
||||
.map(this::findExpansionInFile)
|
||||
.collect(Futures.collector());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public CompletableFuture<List<Class<? extends PlaceholderExpansion>>> findExpansionsInFile(@NotNull final File file)
|
||||
public CompletableFuture<@Nullable Class<? extends PlaceholderExpansion>> findExpansionInFile(@NotNull final File file)
|
||||
{
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try
|
||||
{
|
||||
final List<@NotNull Class<? extends PlaceholderExpansion>> classes = FileUtil.getClasses(getExpansionsFolder(), PlaceholderExpansion.class, file.getName());
|
||||
if (classes.size() > 1)
|
||||
{
|
||||
throw new IllegalStateException("multiple expansion classes in one file!");
|
||||
return FileUtil.findClass(file, PlaceholderExpansion.class);
|
||||
}
|
||||
|
||||
return classes;
|
||||
catch (final VerifyError ex)
|
||||
{
|
||||
plugin.getLogger().severe("expansion file " + file.getName() + " is outdated: \n" +
|
||||
"Failed to load due to a [" + ex.getClass().getSimpleName() + "], attempted to use" + ex.getMessage().substring(ex.getMessage().lastIndexOf(' ')));
|
||||
return null;
|
||||
}
|
||||
catch (final Exception ex)
|
||||
{
|
||||
@ -365,6 +358,11 @@ public final class LocalExpansionManager implements Listener
|
||||
}
|
||||
catch (final Exception ex)
|
||||
{
|
||||
if (ex.getCause() instanceof LinkageError)
|
||||
{
|
||||
throw ((LinkageError) ex.getCause());
|
||||
}
|
||||
|
||||
plugin.getLogger().log(Level.SEVERE, "Failed to load placeholder expansion from class: " + clazz.getName(), ex);
|
||||
return null;
|
||||
}
|
||||
|
@ -24,51 +24,25 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarInputStream;
|
||||
|
||||
public class FileUtil
|
||||
{
|
||||
|
||||
@NotNull
|
||||
public static <T> List<@NotNull Class<? extends T>> getClasses(@NotNull final File folder, @NotNull final Class<T> clazz) throws IOException, ClassNotFoundException
|
||||
@Nullable
|
||||
public static <T> Class<? extends T> findClass(@NotNull final File file, @NotNull final Class<T> clazz) throws IOException, ClassNotFoundException
|
||||
{
|
||||
return getClasses(folder, clazz, null);
|
||||
if (!file.exists())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static <T> List<@NotNull Class<? extends T>> getClasses(@NotNull final File folder, @NotNull final Class<T> clazz, @Nullable final String target) throws IOException, ClassNotFoundException
|
||||
{
|
||||
if (!folder.exists())
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final URL jar = file.toURI().toURL();
|
||||
|
||||
final File[] jars = folder.listFiles((dir, name) -> name.endsWith(".jar") && (target == null || name.replace(".jar", "").equalsIgnoreCase(target.replace(".jar", ""))));
|
||||
if (jars == null)
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final List<@NotNull Class<? extends T>> list = new ArrayList<>();
|
||||
|
||||
for (final File file : jars)
|
||||
{
|
||||
gather(file.toURI().toURL(), clazz, list);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private static <T> void gather(@NotNull final URL jar, @NotNull final Class<T> clazz, @NotNull final List<@NotNull Class<? extends T>> list) throws IOException, ClassNotFoundException
|
||||
{
|
||||
try (final URLClassLoader loader = new URLClassLoader(new URL[]{jar}, clazz.getClassLoader()); final JarInputStream stream = new JarInputStream(jar.openStream()))
|
||||
{
|
||||
JarEntry entry;
|
||||
@ -85,7 +59,7 @@ public class FileUtil
|
||||
final Class<?> loaded = loader.loadClass(name.substring(0, name.lastIndexOf('.')).replace('/', '.'));
|
||||
if (clazz.isAssignableFrom(loaded))
|
||||
{
|
||||
list.add(loaded.asSubclass(clazz));
|
||||
return loaded.asSubclass(clazz);
|
||||
}
|
||||
}
|
||||
catch (final NoClassDefFoundError ignored)
|
||||
@ -93,6 +67,8 @@ public class FileUtil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
63
src/main/java/me/clip/placeholderapi/util/Futures.java
Normal file
63
src/main/java/me/clip/placeholderapi/util/Futures.java
Normal file
@ -0,0 +1,63 @@
|
||||
package me.clip.placeholderapi.util;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.stream.Collector;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class Futures
|
||||
{
|
||||
|
||||
private Futures()
|
||||
{}
|
||||
|
||||
|
||||
public static <T> void onMainThread(@NotNull final Plugin plugin, @NotNull final CompletableFuture<T> future, @NotNull final BiConsumer<T, Throwable> consumer)
|
||||
{
|
||||
future.whenComplete((value, exception) -> {
|
||||
if (Bukkit.isPrimaryThread())
|
||||
{
|
||||
consumer.accept(value, exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
Bukkit.getScheduler().runTask(plugin, () -> consumer.accept(value, exception));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public static <T> Collector<CompletableFuture<T>, ?, CompletableFuture<List<T>>> collector()
|
||||
{
|
||||
return Collectors.collectingAndThen(Collectors.toList(), Futures::of);
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public static <T> CompletableFuture<List<T>> of(@NotNull final Stream<CompletableFuture<T>> futures)
|
||||
{
|
||||
return of(futures.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static <T> CompletableFuture<List<T>> of(@NotNull final Collection<CompletableFuture<T>> futures)
|
||||
{
|
||||
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
|
||||
.thenApplyAsync($ -> awaitCompletion(futures));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static <T> List<T> awaitCompletion(@NotNull final Collection<CompletableFuture<T>> futures)
|
||||
{
|
||||
return futures.stream().map(CompletableFuture::join).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user