From 960732b9f0906fb83a7095114b4e71be956eb54a Mon Sep 17 00:00:00 2001 From: Bukkit/Spigot Date: Mon, 2 May 2011 11:31:00 -0700 Subject: [PATCH] Added services manager framework. Services are interfaces that specifies capabilities to be implemented by providers. Example services include economy, , etc. By: sk89q --- .../src/main/java/org/bukkit/Server.java | 8 + .../plugin/RegisteredServiceProvider.java | 50 +++ .../org/bukkit/plugin/ServicePriority.java | 12 + .../org/bukkit/plugin/ServicesManager.java | 110 +++++++ .../bukkit/plugin/SimplePluginManager.java | 1 + .../bukkit/plugin/SimpleServicesManager.java | 288 ++++++++++++++++++ 6 files changed, 469 insertions(+) create mode 100644 paper-api/src/main/java/org/bukkit/plugin/RegisteredServiceProvider.java create mode 100644 paper-api/src/main/java/org/bukkit/plugin/ServicePriority.java create mode 100644 paper-api/src/main/java/org/bukkit/plugin/ServicesManager.java create mode 100644 paper-api/src/main/java/org/bukkit/plugin/SimpleServicesManager.java diff --git a/paper-api/src/main/java/org/bukkit/Server.java b/paper-api/src/main/java/org/bukkit/Server.java index 01c5726c6f..e60e2a9744 100644 --- a/paper-api/src/main/java/org/bukkit/Server.java +++ b/paper-api/src/main/java/org/bukkit/Server.java @@ -11,6 +11,7 @@ import org.bukkit.command.PluginCommand; import org.bukkit.command.CommandSender; import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.ServicesManager; import org.bukkit.scheduler.BukkitScheduler; /** @@ -118,6 +119,13 @@ public interface Server { */ public BukkitScheduler getScheduler(); + /** + * Gets a services manager + * + * @return Services manager + */ + public ServicesManager getServicesManager(); + /** * Gets a list of all worlds on this server * diff --git a/paper-api/src/main/java/org/bukkit/plugin/RegisteredServiceProvider.java b/paper-api/src/main/java/org/bukkit/plugin/RegisteredServiceProvider.java new file mode 100644 index 0000000000..e43ad7e5f8 --- /dev/null +++ b/paper-api/src/main/java/org/bukkit/plugin/RegisteredServiceProvider.java @@ -0,0 +1,50 @@ +package org.bukkit.plugin; + +/** + * A registered service provider. + * + * @author sk89q + * @param Service + */ +public class RegisteredServiceProvider + implements Comparable> { + + private Class service; + private Plugin plugin; + private T provider; + private ServicePriority priority; + + public RegisteredServiceProvider(Class service, T provider, + ServicePriority priority, Plugin plugin) { + + this.service = service; + this.plugin = plugin; + this.provider = provider; + this.priority = priority; + } + + public Class getService() { + return service; + } + + public Plugin getPlugin() { + return plugin; + } + + public T getProvider() { + return provider; + } + + public ServicePriority getPriority() { + return priority; + } + + public int compareTo(RegisteredServiceProvider other) { + if (priority.ordinal() == other.getPriority().ordinal()) { + return 0; + } else { + return priority.ordinal() < other.getPriority().ordinal() ? 1 : -1; + } + } + +} diff --git a/paper-api/src/main/java/org/bukkit/plugin/ServicePriority.java b/paper-api/src/main/java/org/bukkit/plugin/ServicePriority.java new file mode 100644 index 0000000000..6f57dbd453 --- /dev/null +++ b/paper-api/src/main/java/org/bukkit/plugin/ServicePriority.java @@ -0,0 +1,12 @@ +package org.bukkit.plugin; + +/** + * Represents various priorities of a provider. + */ +public enum ServicePriority { + Lowest, + Low, + Normal, + High, + Highest +} \ No newline at end of file diff --git a/paper-api/src/main/java/org/bukkit/plugin/ServicesManager.java b/paper-api/src/main/java/org/bukkit/plugin/ServicesManager.java new file mode 100644 index 0000000000..6e04a32b59 --- /dev/null +++ b/paper-api/src/main/java/org/bukkit/plugin/ServicesManager.java @@ -0,0 +1,110 @@ +package org.bukkit.plugin; + +import java.util.Collection; +import java.util.List; + +/** + * Manages services and service providers. Services are an interface specifying + * a list of methods that a provider must implement. Providers are + * implementations of these services. A provider can be queried from the + * services manager in order to use a service (if one is available). If + * multiple plugins register a service, then the service with the highest + * priority takes precedence. + * + * @author sk89q + */ +public interface ServicesManager { + + /** + * Register a provider of a service. + * + * @param Provider + * @param service service class + * @param provider provider to register + * @param plugin plugin with the provider + * @param priority priority of the provider + */ + public void register(Class service, T provider, Plugin plugin, + ServicePriority priority); + + /** + * Unregister all the providers registered by a particular plugin. + * + * @param plugin + */ + public void unregisterAll(Plugin plugin); + + /** + * Unregister a particular provider for a particular service. + * + * @param service + * @param provider + */ + public void unregister(Class service, Object provider); + + /** + * Unregister a particular provider. + * + * @param provider + */ + public void unregister(Object provider); + + /** + * Queries for a provider. This may return if no provider has been + * registered for a service. The highest priority provider is returned. + * + * @param + * @param service + * @return provider or null + */ + public T load(Class service); + + /** + * Queries for a provider registration. This may return if no provider + * has been registered for a service. + * + * @param + * @param service + * @return provider registration or null + */ + public RegisteredServiceProvider getRegistration(Class service); + + /** + * Get registrations of providers for a plugin. + * + * @param plugin + * @return provider registration or null + */ + public List> getRegistrations(Plugin plugin); + + /** + * Get registrations of providers for a service. The returned list is + * unmodifiable. + * + * @param + * @param service + * @return list of registrations + */ + public Collection> getRegistrations( + Class service); + + /** + * Get a list of known services. A service is known if it has registered + * providers for it. + * + * @return list of known services + */ + public Collection> getKnownServices(); + + /** + * Returns whether a provider has been registered for a service. Do not + * check this first only to call load(service) later, as that + * would be a non-thread safe situation. + * + * @param service + * @param service service to check + * @return whether there has been a registered provider + */ + public boolean isProvidedFor(Class service); + +} \ No newline at end of file diff --git a/paper-api/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/paper-api/src/main/java/org/bukkit/plugin/SimplePluginManager.java index ffdbb5fd75..38345b26ad 100644 --- a/paper-api/src/main/java/org/bukkit/plugin/SimplePluginManager.java +++ b/paper-api/src/main/java/org/bukkit/plugin/SimplePluginManager.java @@ -245,6 +245,7 @@ public final class SimplePluginManager implements PluginManager { if (plugin.isEnabled()) { plugin.getPluginLoader().disablePlugin(plugin); server.getScheduler().cancelTasks(plugin); + server.getServicesManager().unregisterAll(plugin); } } diff --git a/paper-api/src/main/java/org/bukkit/plugin/SimpleServicesManager.java b/paper-api/src/main/java/org/bukkit/plugin/SimpleServicesManager.java new file mode 100644 index 0000000000..ce55736592 --- /dev/null +++ b/paper-api/src/main/java/org/bukkit/plugin/SimpleServicesManager.java @@ -0,0 +1,288 @@ +package org.bukkit.plugin; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.List; +import java.util.NoSuchElementException; + +/** + * A simple services manager. + * + * @author sk89q + */ +public class SimpleServicesManager implements ServicesManager { + + /** + * Map of providers. + */ + private final Map, List>> providers = + new HashMap, List>>(); + + /** + * Register a provider of a service. + * + * @param Provider + * @param service service class + * @param provider provider to register + * @param plugin plugin with the provider + * @param priority priority of the provider + */ + public void register(Class service, T provider, + Plugin plugin, ServicePriority priority) { + + synchronized (providers) { + List> registered = + providers.get(service); + + if (registered == null) { + registered = new ArrayList>(); + providers.put(service, registered); + } + + registered.add(new RegisteredServiceProvider( + service, provider, priority, plugin)); + + // Make sure that providers are in the right order in order + // for priorities to work correctly + Collections.sort(registered); + } + } + + /** + * Unregister all the providers registered by a particular plugin. + * + * @param plugin + */ + public void unregisterAll(Plugin plugin) { + synchronized (providers) { + Iterator, List>>> it = + providers.entrySet().iterator(); + + try { + while (it.hasNext()) { + Map.Entry, List>> entry = it.next(); + Iterator> it2 = entry.getValue().iterator(); + + try { + // Removed entries that are from this plugin + while (it2.hasNext()) { + if (it2.next().getPlugin() == plugin) { + it2.remove(); + } + } + } catch (NoSuchElementException e) { + // Why does Java suck + } + + // Get rid of the empty list + if (entry.getValue().size() == 0) { + it.remove(); + } + } + } catch (NoSuchElementException e) { + } + } + } + + /** + * Unregister a particular provider for a particular service. + * + * @param service + * @param provider + */ + public void unregister(Class service, Object provider) { + synchronized (providers) { + Iterator, List>>> it = + providers.entrySet().iterator(); + + try { + while (it.hasNext()) { + Map.Entry, List>> entry = it.next(); + + // We want a particular service + if (entry.getKey() != service) { + continue; + } + + Iterator> it2 = entry.getValue().iterator(); + + try { + // Removed entries that are from this plugin + while (it2.hasNext()) { + if (it2.next().getProvider() == provider) { + it2.remove(); + } + } + } catch (NoSuchElementException e) { + // Why does Java suck + } + + // Get rid of the empty list + if (entry.getValue().size() == 0) { + it.remove(); + } + } + } catch (NoSuchElementException e) { + } + } + } + + /** + * Unregister a particular provider. + * + * @param provider + */ + public void unregister(Object provider) { + synchronized (providers) { + Iterator, List>>> it = + providers.entrySet().iterator(); + + try { + while (it.hasNext()) { + Map.Entry, List>> entry = it.next(); + Iterator> it2 = entry.getValue().iterator(); + + try { + // Removed entries that are from this plugin + while (it2.hasNext()) { + if (it2.next().getProvider() == provider) { + it2.remove(); + } + } + } catch (NoSuchElementException e) { + // Why does Java suck + } + + // Get rid of the empty list + if (entry.getValue().size() == 0) { + it.remove(); + } + } + } catch (NoSuchElementException e) { + } + } + } + + /** + * Queries for a provider. This may return if no provider has been + * registered for a service. The highest priority provider is returned. + * + * @param + * @param service + * @return provider or null + */ + @SuppressWarnings("unchecked") + public T load(Class service) { + synchronized (providers) { + List> registered = providers.get(service); + + if (registered == null) { + return null; + } + + // This should not be null! + return (T) registered.get(0).getProvider(); + } + } + + /** + * Queries for a provider registration. This may return if no provider + * has been registered for a service. + * + * @param + * @param service + * @return provider registration or null + */ + @SuppressWarnings("unchecked") + public RegisteredServiceProvider getRegistration(Class service) { + synchronized (providers) { + List> registered = providers.get(service); + + if (registered == null) { + return null; + } + + // This should not be null! + return (RegisteredServiceProvider) registered.get(0); + } + } + + /** + * Get registrations of providers for a plugin. + * + * @param plugin + * @return provider registration or null + */ + public List> getRegistrations(Plugin plugin) { + synchronized (providers) { + List> ret = + new ArrayList>(); + + for (List> registered : providers.values()) { + for (RegisteredServiceProvider provider : registered) { + if (provider.getPlugin() == plugin) { + ret.add(provider); + } + } + } + + return ret; + } + } + + /** + * Get registrations of providers for a service. The returned list is + * unmodifiable. + * + * @param + * @param service + * @return list of registrations + */ + @SuppressWarnings("unchecked") + public Collection> getRegistrations(Class service) { + synchronized (providers) { + List> registered = providers.get(service); + + if (registered == null) { + return Collections.unmodifiableList( + new ArrayList>()); + } + + List> ret = + new ArrayList>(); + + for (RegisteredServiceProvider provider : registered) { + ret.add((RegisteredServiceProvider) provider); + } + + return Collections.unmodifiableList(ret); + } + } + + /** + * Get a list of known services. A service is known if it has registered + * providers for it. + * + * @return list of known services + */ + public Collection> getKnownServices() { + return Collections.unmodifiableSet(providers.keySet()); + } + + /** + * Returns whether a provider has been registered for a service. Do not + * check this first only to call load(service) later, as that + * would be a non-thread safe situation. + * + * @param service + * @param service service to check + * @return whether there has been a registered provider + */ + public boolean isProvidedFor(Class service) { + return getRegistration(service) != null; + } +}