Skip to content

Commit

Permalink
Rework bolt so uncertain operations like creating plugins return Opti…
Browse files Browse the repository at this point in the history
…onal
  • Loading branch information
nathanielsherry committed Jun 4, 2024
1 parent e965435 commit 03489ba
Show file tree
Hide file tree
Showing 33 changed files with 268 additions and 142 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
import java.util.Scanner;
import java.util.logging.Level;

Expand All @@ -10,6 +11,7 @@
import org.peakaboo.framework.bolt.plugin.core.PluginDescriptor;
import org.peakaboo.framework.bolt.plugin.core.PluginRegistry;
import org.peakaboo.framework.bolt.plugin.core.container.BoltContainer;
import org.peakaboo.framework.bolt.plugin.core.exceptions.BoltException;

public class BoltConfigPluginDescriptor<T extends BoltConfigPlugin> implements PluginDescriptor<T> {

Expand All @@ -20,13 +22,20 @@ public class BoltConfigPluginDescriptor<T extends BoltConfigPlugin> implements P
private PluginRegistry<T> registry;
private int weight = PluginDescriptor.WEIGHT_MEDIUM;

public BoltConfigPluginDescriptor(PluginRegistry<T> registry, BoltConfigPluginBuilder<T> builder, Class<T> pluginClass, BoltConfigContainer<T> container, int weight) {
public BoltConfigPluginDescriptor(PluginRegistry<T> registry, BoltConfigPluginBuilder<T> builder, Class<T> pluginClass, BoltConfigContainer<T> container, int weight) throws BoltException {
this.builder = builder;
this.pluginClass = pluginClass;
this.container = container;
this.registry = registry;
this.weight = weight;
this.reference = create();

var creation = create();
if (creation.isPresent()) {
this.reference = creation.get();
} else {
throw new BoltException("Coult not create reference instance for plugin " + pluginClass.getName());
}

}

@Override
Expand All @@ -40,7 +49,7 @@ public Class<T> getPluginClass() {
}

@Override
public T create() {
public Optional<T> create() {

try (InputStream stream = container.openStream()) {

Expand All @@ -54,10 +63,10 @@ public T create() {
throw new IOException("Could not read file contents");
}
s.close();
return plugin;
return Optional.of(plugin);
} catch (IOException e) {
Bolt.logger().log(Level.FINE, "Could not create plugin instance: " + container.getSourceName(), e);
return null;
return Optional.empty();
}


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.peakaboo.framework.bolt.plugin.config.container;

import org.peakaboo.framework.bolt.plugin.config.BoltConfigPlugin;
import org.peakaboo.framework.bolt.plugin.core.container.BoltContainer;
import org.peakaboo.framework.bolt.plugin.core.issue.BoltBrokenContainerIssue;
import org.peakaboo.framework.bolt.plugin.java.BoltJavaPlugin;
import org.peakaboo.framework.bolt.plugin.java.container.BoltJarContainer;

public class BoltBrokenConfigfileIssue<T extends BoltConfigPlugin> extends BoltBrokenContainerIssue<T> {

private String message;

public BoltBrokenConfigfileIssue(BoltConfigContainer<T> container, String message) {
super(container);
this.message = message;
}

@Override
public String title() {
return "Broken Configfile Container";
}

@Override
public String description() {
return "Could not read plugin configfile " + shortSource() + ": " + message;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import org.peakaboo.framework.bolt.plugin.config.BoltConfigPlugin;
Expand All @@ -12,12 +13,18 @@
import org.peakaboo.framework.bolt.plugin.core.PluginDescriptor;
import org.peakaboo.framework.bolt.plugin.core.PluginRegistry;
import org.peakaboo.framework.bolt.plugin.core.container.BoltURLContainer;
import org.peakaboo.framework.bolt.plugin.core.exceptions.BoltException;
import org.peakaboo.framework.bolt.plugin.core.issue.BoltIssue;

/**
* Implementation of the BoltContainer interface which handles loading a
* single config plugin from a file.
*/
public class BoltConfigContainer<T extends BoltConfigPlugin> extends BoltURLContainer<T>{

private BoltPluginSet<T> plugins;
private PluginRegistry<T> manager;
private List<BoltIssue<T>> issues;

public BoltConfigContainer(PluginRegistry<T> manager, URL url, Class<T> pluginClass, BoltConfigPluginBuilder<T> builder, boolean deletable) {
super(url, deletable);
Expand All @@ -26,24 +33,32 @@ public BoltConfigContainer(PluginRegistry<T> manager, URL url, Class<T> pluginCl
}
this.url = url;
this.manager = manager;
this.issues = new ArrayList<>();

plugins = new BoltPluginSet<>(manager);
BoltConfigPluginDescriptor<T> plugin = new BoltConfigPluginDescriptor<>(this.manager, builder, pluginClass, this, PluginDescriptor.WEIGHT_MEDIUM);
plugins.addPlugin(plugin);
try {
BoltConfigPluginDescriptor<T> plugin = new BoltConfigPluginDescriptor<>(this.manager, builder, pluginClass, this, PluginDescriptor.WEIGHT_MEDIUM);
plugins.addPlugin(plugin);
} catch (BoltException e) {
issues.add(new BoltBrokenConfigfileIssue<>(this, "Could not load plugin from file"));
}
}

public InputStream openStream() throws IOException {
return url.openStream();
}

@Override
public List<PluginDescriptor<? extends T>> getPlugins() {
public List<PluginDescriptor<T>> getPlugins() {
return plugins.getPlugins();
}

@Override
public List<BoltIssue<? extends T>> getIssues() {
return plugins.getIssues();
public List<BoltIssue<T>> getIssues() {
var list = new ArrayList<BoltIssue<T>>();
list.addAll(plugins.getIssues());
list.addAll(issues);
return list;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import org.peakaboo.framework.bolt.plugin.core.container.BoltContainer;
Expand Down Expand Up @@ -77,29 +78,29 @@ public String getAssetPath() {
}

@Override
public final synchronized List<PluginDescriptor<? extends P>> getPlugins() {
public final synchronized List<PluginDescriptor<P>> getPlugins() {
load();
return plugins.getPlugins();
}

@Override
public List<BoltIssue<? extends P>> getIssues() {
public List<BoltIssue<P>> getIssues() {
/*
* Here we not only return the issues generated by containers during loading,
* but we also return issues detected from a higher-level view such as one
* container having only out-of-date plugins, which we can't know until we see
* all the containers at once. This later set is constructed on the fly so that
* we're always generating issues based on the current set of containers.
*/
List<BoltIssue<? extends P>> allIssues = new ArrayList<>();
List<BoltIssue<P>> allIssues = new ArrayList<>();
allIssues.addAll(plugins.getIssues());
allIssues.addAll(findIssues());
return allIssues;
}


private List<BoltIssue<? extends P>> findIssues() {
List<BoltIssue<? extends P>> found = new ArrayList<>();
private List<BoltIssue<P>> findIssues() {
List<BoltIssue<P>> found = new ArrayList<>();

//check if a container contains only outdated plugins
for (BoltContainer<P> container : containers) {
Expand All @@ -108,10 +109,10 @@ private List<BoltIssue<? extends P>> findIssues() {
//we're not responsible for detecting empty containers
if (container.isEmpty()) { continue; }

for (PluginDescriptor<? extends P> plugin : container.getPlugins()) {
for (PluginDescriptor<P> plugin : container.getPlugins()) {
//look up the newest version of this plugin by UUID
PluginDescriptor<? extends P> newest = getByUUID(plugin.getUUID());
if (newest.isNewerThan(plugin)) {
Optional<PluginDescriptor<P>> lookup = getByUUID(plugin.getUUID());
if (lookup.isPresent() && lookup.get().isNewerThan(plugin)) {
outdated = true;
}
}
Expand All @@ -125,9 +126,8 @@ private List<BoltIssue<? extends P>> findIssues() {


@Override
@SuppressWarnings("unchecked")
public void addLoader(BoltLoader<? extends P> loader) {
loaders.add((BoltLoader<P>) loader);
public void addLoader(BoltLoader<P> loader) {
loaders.add(loader);
}

private List<BoltManagedLoader<P>> managedLoaders() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
public class BoltPluginSet<T extends BoltPlugin> implements PluginCollection<T> {

//Anything modifying this plugins list should call sort() afterwards
private ArrayList<PluginDescriptor<? extends T>> plugins = new ArrayList<>();
private ArrayList<BoltIssue<? extends T>> issues = new ArrayList<>();
private ArrayList<PluginDescriptor<T>> plugins = new ArrayList<>();
private ArrayList<BoltIssue<T>> issues = new ArrayList<>();

private PluginRegistry<T> manager;

Expand All @@ -24,19 +24,20 @@ public BoltPluginSet(PluginRegistry<T> manager) {
}

@Override
public List<PluginDescriptor<? extends T>> getPlugins() {
public List<PluginDescriptor<T>> getPlugins() {
return Collections.unmodifiableList(plugins);
}

public void addPlugin(PluginDescriptor<? extends T> plugin) {
public void addPlugin(PluginDescriptor<T> plugin) {
if (plugins.contains(plugin)) {
return;
}
String uuid = plugin.getUUID();
if (this.hasUUID(uuid)) {
var lookup = this.getByUUID(uuid);
if (lookup.isPresent()) {
//there is already a plugin with the same UUID.
//we have to choose which of these to load
PluginDescriptor<? extends T> existingPlugin = this.getByUUID(uuid);
PluginDescriptor<T> existingPlugin = lookup.get();

if (plugin.isUpgradeFor(existingPlugin)) {
plugins.remove(existingPlugin);
Expand All @@ -54,24 +55,24 @@ public void addPlugin(PluginDescriptor<? extends T> plugin) {

}

public void loadFrom(PluginCollection<? extends T> pluginset) {
for (PluginDescriptor<? extends T> t : pluginset.getPlugins()) {
public void loadFrom(PluginCollection<T> pluginset) {
for (var t : pluginset.getPlugins()) {
addPlugin(t);
}
for (BoltIssue<? extends T> i : pluginset.getIssues()) {
for (var i : pluginset.getIssues()) {
addIssue(i);
}
}



public List<BoltIssue<? extends T>> getIssues() {
public List<BoltIssue<T>> getIssues() {
return Collections.unmodifiableList(issues);
}



public void addIssue(BoltIssue<? extends T> issue) {
public void addIssue(BoltIssue<T> issue) {
issues.add(issue);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,39 @@
* Interface for exposing a set of {@link BoltPlugin}s and {@link BoltIssue}s
* in a read-only way.
*/
public interface PluginCollection<T extends BoltPlugin> extends Iterable<PluginDescriptor<? extends T>> {
public interface PluginCollection<T extends BoltPlugin> extends Iterable<PluginDescriptor<T>> {

List<PluginDescriptor<? extends T>> getPlugins();
List<PluginDescriptor<T>> getPlugins();

default Iterator<PluginDescriptor<? extends T>> iterator() {
default Iterator<PluginDescriptor<T>> iterator() {
return getPlugins().iterator();
}

List<BoltIssue<? extends T>> getIssues();
List<BoltIssue<T>> getIssues();

default List<T> newInstances() {
List<T> insts = getPlugins().stream().map(p -> p.create()).collect(Collectors.toList());
List<T> insts = getPlugins()
.stream()
.map(p -> p.create())
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
Collections.sort(insts, (f1, f2) -> f1.pluginName().compareTo(f1.pluginName()));
return insts;
}

PluginRegistry<T> getManager();

default PluginDescriptor<? extends T> getByUUID(String uuid) {
for (PluginDescriptor<? extends T> plugin : getPlugins()) {
default Optional<PluginDescriptor<T>> getByUUID(String uuid) {
for (PluginDescriptor<T> plugin : getPlugins()) {
if (plugin.getUUID().equals(uuid)) {
return plugin;
return Optional.of(plugin);
}
}
return null;
return Optional.empty();
}

default Optional<PluginDescriptor<? extends T>> getByClass(Class<? extends T> cls) {
default Optional<PluginDescriptor<T>> getByClass(Class<? extends T> cls) {
synchronized(this) {
for (var plugin : getPlugins()) {
if (plugin.getReferenceInstance().getClass().equals(cls)) {
Expand All @@ -51,7 +56,7 @@ default Optional<PluginDescriptor<? extends T>> getByClass(Class<? extends T> cl
}

default boolean hasUUID(String uuid) {
return getByUUID(uuid) != null;
return getByUUID(uuid).isPresent();
}

default int size() {
Expand All @@ -72,17 +77,31 @@ default boolean isEmpty() {
* @param other the other collection to compare against
* @return true if this collection is a proper upgrade for the other, false otherwise
*/
default boolean isUpgradeFor(PluginCollection<? extends T> other) {
default boolean isUpgradeFor(PluginCollection<T> other) {
//get all the UUIDs from the other plugin set
List<String> otherUUIDs = other.getPlugins().stream().map(p -> p.getUUID()).collect(Collectors.toList());

// For every uuid in THEIR collection, we wee if OUR collection
// also has that uuid. If we do, we must check if OUR matching
// plugin is an upgrade for THEIR plugin
//if this set is missing any of the UUIDs, it's not an upgrade
for (String otherUUID : otherUUIDs) {
if (!this.hasUUID(otherUUID)) {
for (String uuid : otherUUIDs) {

// Try to get OUR plugin for this uuid
var ourLookup = this.getByUUID(uuid);
if (ourLookup.isEmpty()) {
return false;
}
boolean isUpgrade = this.getByUUID(otherUUID).isUpgradeFor(other.getByUUID(otherUUID));
if (!isUpgrade) {
var ourPlugin = ourLookup.get();

// Try to get THEIR plugin for this uuid
var theirLookup = other.getByUUID(uuid);
if (theirLookup.isEmpty()) {
return false;
}
var theirPlugin = theirLookup.get();

if ( ! ourPlugin.isUpgradeFor(theirPlugin) ) {
return false;
}
}
Expand Down
Loading

0 comments on commit 03489ba

Please sign in to comment.