package org.bukkit.plugin.messaging; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet.Builder; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; /** * Standard implementation to {@link Messenger} */ public class StandardMessenger implements Messenger { private final Map> incomingByChannel = new HashMap>(); private final Map> incomingByPlugin = new HashMap>(); private final Map> outgoingByChannel = new HashMap>(); private final Map> outgoingByPlugin = new HashMap>(); private final Object incomingLock = new Object(); private final Object outgoingLock = new Object(); private void addToOutgoing(Plugin plugin, String channel) { synchronized (outgoingLock) { Set plugins = outgoingByChannel.get(channel); Set channels = outgoingByPlugin.get(plugin); if (plugins == null) { plugins = new HashSet(); outgoingByChannel.put(channel, plugins); } if (channels == null) { channels = new HashSet(); outgoingByPlugin.put(plugin, channels); } plugins.add(plugin); channels.add(channel); } } private void removeFromOutgoing(Plugin plugin, String channel) { synchronized (outgoingLock) { Set plugins = outgoingByChannel.get(channel); Set channels = outgoingByPlugin.get(plugin); if (plugins != null) { plugins.remove(plugin); if (plugins.isEmpty()) { outgoingByChannel.remove(channel); } } if (channels != null) { channels.remove(channel); if (channels.isEmpty()) { outgoingByChannel.remove(channel); } } } } private void removeFromOutgoing(Plugin plugin) { synchronized (outgoingLock) { Set channels = outgoingByPlugin.get(plugin); if (channels != null) { String[] toRemove = channels.toArray(new String[0]); outgoingByPlugin.remove(plugin); for (String channel : toRemove) { removeFromOutgoing(plugin, channel); } } } } private void addToIncoming(PluginMessageListenerRegistration registration) { synchronized (incomingLock) { Set registrations = incomingByChannel.get(registration.getChannel()); if (registrations == null) { registrations = new HashSet(); incomingByChannel.put(registration.getChannel(), registrations); } else { if (registrations.contains(registration)) { throw new IllegalArgumentException("This registration already exists"); } } registrations.add(registration); registrations = incomingByPlugin.get(registration.getPlugin()); if (registrations == null) { registrations = new HashSet(); incomingByPlugin.put(registration.getPlugin(), registrations); } else { if (registrations.contains(registration)) { throw new IllegalArgumentException("This registration already exists"); } } registrations.add(registration); } } private void removeFromIncoming(PluginMessageListenerRegistration registration) { synchronized (incomingLock) { Set registrations = incomingByChannel.get(registration.getChannel()); if (registrations != null) { registrations.remove(registration); if (registrations.isEmpty()) { incomingByChannel.remove(registration.getChannel()); } } registrations = incomingByPlugin.get(registration.getPlugin()); if (registrations != null) { registrations.remove(registration); if (registrations.isEmpty()) { incomingByPlugin.remove(registration.getPlugin()); } } } } private void removeFromIncoming(Plugin plugin, String channel) { synchronized (incomingLock) { Set registrations = incomingByPlugin.get(plugin); if (registrations != null) { PluginMessageListenerRegistration[] toRemove = registrations.toArray(new PluginMessageListenerRegistration[0]); for (PluginMessageListenerRegistration registration : toRemove) { if (registration.getChannel().equals(channel)) { removeFromIncoming(registration); } } } } } private void removeFromIncoming(Plugin plugin) { synchronized (incomingLock) { Set registrations = incomingByPlugin.get(plugin); if (registrations != null) { PluginMessageListenerRegistration[] toRemove = registrations.toArray(new PluginMessageListenerRegistration[0]); incomingByPlugin.remove(plugin); for (PluginMessageListenerRegistration registration : toRemove) { removeFromIncoming(registration); } } } } public boolean isReservedChannel(String channel) { validateChannel(channel); return channel.equals("REGISTER") || channel.equals("UNREGISTER"); } public void registerOutgoingPluginChannel(Plugin plugin, String channel) { if (plugin == null) { throw new IllegalArgumentException("Plugin cannot be null"); } validateChannel(channel); if (isReservedChannel(channel)) { throw new ReservedChannelException(channel); } addToOutgoing(plugin, channel); } public void unregisterOutgoingPluginChannel(Plugin plugin, String channel) { if (plugin == null) { throw new IllegalArgumentException("Plugin cannot be null"); } validateChannel(channel); removeFromOutgoing(plugin, channel); } public void unregisterOutgoingPluginChannel(Plugin plugin) { if (plugin == null) { throw new IllegalArgumentException("Plugin cannot be null"); } removeFromOutgoing(plugin); } public PluginMessageListenerRegistration registerIncomingPluginChannel(Plugin plugin, String channel, PluginMessageListener listener) { if (plugin == null) { throw new IllegalArgumentException("Plugin cannot be null"); } validateChannel(channel); if (isReservedChannel(channel)) { throw new ReservedChannelException(channel); } if (listener == null) { throw new IllegalArgumentException("Listener cannot be null"); } PluginMessageListenerRegistration result = new PluginMessageListenerRegistration(this, plugin, channel, listener); addToIncoming(result); return result; } public void unregisterIncomingPluginChannel(Plugin plugin, String channel, PluginMessageListener listener) { if (plugin == null) { throw new IllegalArgumentException("Plugin cannot be null"); } if (listener == null) { throw new IllegalArgumentException("Listener cannot be null"); } validateChannel(channel); removeFromIncoming(new PluginMessageListenerRegistration(this, plugin, channel, listener)); } public void unregisterIncomingPluginChannel(Plugin plugin, String channel) { if (plugin == null) { throw new IllegalArgumentException("Plugin cannot be null"); } validateChannel(channel); removeFromIncoming(plugin, channel); } public void unregisterIncomingPluginChannel(Plugin plugin) { if (plugin == null) { throw new IllegalArgumentException("Plugin cannot be null"); } removeFromIncoming(plugin); } public Set getOutgoingChannels() { synchronized (outgoingLock) { Set keys = outgoingByChannel.keySet(); return ImmutableSet.copyOf(keys); } } public Set getOutgoingChannels(Plugin plugin) { if (plugin == null) { throw new IllegalArgumentException("Plugin cannot be null"); } synchronized (outgoingLock) { Set channels = outgoingByPlugin.get(plugin); if (channels != null) { return ImmutableSet.copyOf(channels); } else { return ImmutableSet.of(); } } } public Set getIncomingChannels() { synchronized (incomingLock) { Set keys = incomingByChannel.keySet(); return ImmutableSet.copyOf(keys); } } public Set getIncomingChannels(Plugin plugin) { if (plugin == null) { throw new IllegalArgumentException("Plugin cannot be null"); } synchronized (incomingLock) { Set registrations = incomingByPlugin.get(plugin); if (registrations != null) { Builder builder = ImmutableSet.builder(); for (PluginMessageListenerRegistration registration : registrations) { builder.add(registration.getChannel()); } return builder.build(); } else { return ImmutableSet.of(); } } } public Set getIncomingChannelRegistrations(Plugin plugin) { if (plugin == null) { throw new IllegalArgumentException("Plugin cannot be null"); } synchronized (incomingLock) { Set registrations = incomingByPlugin.get(plugin); if (registrations != null) { return ImmutableSet.copyOf(registrations); } else { return ImmutableSet.of(); } } } public Set getIncomingChannelRegistrations(String channel) { validateChannel(channel); synchronized (incomingLock) { Set registrations = incomingByChannel.get(channel); if (registrations != null) { return ImmutableSet.copyOf(registrations); } else { return ImmutableSet.of(); } } } public Set getIncomingChannelRegistrations(Plugin plugin, String channel) { if (plugin == null) { throw new IllegalArgumentException("Plugin cannot be null"); } validateChannel(channel); synchronized (incomingLock) { Set registrations = incomingByPlugin.get(plugin); if (registrations != null) { Builder builder = ImmutableSet.builder(); for (PluginMessageListenerRegistration registration : registrations) { if (registration.getChannel().equals(channel)) { builder.add(registration); } } return builder.build(); } else { return ImmutableSet.of(); } } } public boolean isRegistrationValid(PluginMessageListenerRegistration registration) { if (registration == null) { throw new IllegalArgumentException("Registration cannot be null"); } synchronized (incomingLock) { Set registrations = incomingByPlugin.get(registration.getPlugin()); if (registrations != null) { return registrations.contains(registration); } return false; } } public boolean isIncomingChannelRegistered(Plugin plugin, String channel) { if (plugin == null) { throw new IllegalArgumentException("Plugin cannot be null"); } validateChannel(channel); synchronized (incomingLock) { Set registrations = incomingByPlugin.get(plugin); if (registrations != null) { for (PluginMessageListenerRegistration registration : registrations) { if (registration.getChannel().equals(channel)) { return true; } } } return false; } } public boolean isOutgoingChannelRegistered(Plugin plugin, String channel) { if (plugin == null) { throw new IllegalArgumentException("Plugin cannot be null"); } validateChannel(channel); synchronized (outgoingLock) { Set channels = outgoingByPlugin.get(plugin); if (channels != null) { return channels.contains(channel); } return false; } } public void dispatchIncomingMessage(Player source, String channel, byte[] message) { if (source == null) { throw new IllegalArgumentException("Player source cannot be null"); } if (message == null) { throw new IllegalArgumentException("Message cannot be null"); } validateChannel(channel); Set registrations = getIncomingChannelRegistrations(channel); for (PluginMessageListenerRegistration registration : registrations) { registration.getListener().onPluginMessageReceived(channel, source, message); } } /** * Validates a Plugin Channel name. * * @param channel Channel name to validate. */ public static void validateChannel(String channel) { if (channel == null) { throw new IllegalArgumentException("Channel cannot be null"); } if (channel.length() > Messenger.MAX_CHANNEL_SIZE) { throw new ChannelNameTooLongException(channel); } } /** * Validates the input of a Plugin Message, ensuring the arguments are all valid. * * @param messenger Messenger to use for validation. * @param source Source plugin of the Message. * @param channel Plugin Channel to send the message by. * @param message Raw message payload to send. * @throws IllegalArgumentException Thrown if the source plugin is disabled. * @throws IllegalArgumentException Thrown if source, channel or message is null. * @throws MessageTooLargeException Thrown if the message is too big. * @throws ChannelNameTooLongException Thrown if the channel name is too long. * @throws ChannelNotRegisteredException Thrown if the channel is not registered for this plugin. */ public static void validatePluginMessage(Messenger messenger, Plugin source, String channel, byte[] message) { if (messenger == null) { throw new IllegalArgumentException("Messenger cannot be null"); } if (source == null) { throw new IllegalArgumentException("Plugin source cannot be null"); } if (!source.isEnabled()) { throw new IllegalArgumentException("Plugin must be enabled to send messages"); } if (message == null) { throw new IllegalArgumentException("Message cannot be null"); } if (!messenger.isOutgoingChannelRegistered(source, channel)) { throw new ChannelNotRegisteredException(channel); } if (message.length > Messenger.MAX_MESSAGE_SIZE) { throw new MessageTooLargeException(message); } validateChannel(channel); } }