001 package org.bukkit.plugin.messaging; 002 003 import com.google.common.collect.ImmutableSet; 004 import com.google.common.collect.ImmutableSet.Builder; 005 import java.util.HashMap; 006 import java.util.HashSet; 007 import java.util.Map; 008 import java.util.Set; 009 import org.bukkit.entity.Player; 010 import org.bukkit.plugin.Plugin; 011 012 /** 013 * Standard implementation to {@link Messenger} 014 */ 015 public class StandardMessenger implements Messenger { 016 private final Map<String, Set<PluginMessageListenerRegistration>> incomingByChannel = new HashMap<String, Set<PluginMessageListenerRegistration>>(); 017 private final Map<Plugin, Set<PluginMessageListenerRegistration>> incomingByPlugin = new HashMap<Plugin, Set<PluginMessageListenerRegistration>>(); 018 private final Map<String, Set<Plugin>> outgoingByChannel = new HashMap<String, Set<Plugin>>(); 019 private final Map<Plugin, Set<String>> outgoingByPlugin = new HashMap<Plugin, Set<String>>(); 020 private final Object incomingLock = new Object(); 021 private final Object outgoingLock = new Object(); 022 023 private void addToOutgoing(Plugin plugin, String channel) { 024 synchronized (outgoingLock) { 025 Set<Plugin> plugins = outgoingByChannel.get(channel); 026 Set<String> channels = outgoingByPlugin.get(plugin); 027 028 if (plugins == null) { 029 plugins = new HashSet<Plugin>(); 030 outgoingByChannel.put(channel, plugins); 031 } 032 033 if (channels == null) { 034 channels = new HashSet<String>(); 035 outgoingByPlugin.put(plugin, channels); 036 } 037 038 plugins.add(plugin); 039 channels.add(channel); 040 } 041 } 042 043 private void removeFromOutgoing(Plugin plugin, String channel) { 044 synchronized (outgoingLock) { 045 Set<Plugin> plugins = outgoingByChannel.get(channel); 046 Set<String> channels = outgoingByPlugin.get(plugin); 047 048 if (plugins != null) { 049 plugins.remove(plugin); 050 051 if (plugins.isEmpty()) { 052 outgoingByChannel.remove(channel); 053 } 054 } 055 056 if (channels != null) { 057 channels.remove(channel); 058 059 if (channels.isEmpty()) { 060 outgoingByChannel.remove(channel); 061 } 062 } 063 } 064 } 065 066 private void removeFromOutgoing(Plugin plugin) { 067 synchronized (outgoingLock) { 068 Set<String> channels = outgoingByPlugin.get(plugin); 069 070 if (channels != null) { 071 String[] toRemove = channels.toArray(new String[0]); 072 073 outgoingByPlugin.remove(plugin); 074 075 for (String channel : toRemove) { 076 removeFromOutgoing(plugin, channel); 077 } 078 } 079 } 080 } 081 082 private void addToIncoming(PluginMessageListenerRegistration registration) { 083 synchronized (incomingLock) { 084 Set<PluginMessageListenerRegistration> registrations = incomingByChannel.get(registration.getChannel()); 085 086 if (registrations == null) { 087 registrations = new HashSet<PluginMessageListenerRegistration>(); 088 incomingByChannel.put(registration.getChannel(), registrations); 089 } else { 090 if (registrations.contains(registration)) { 091 throw new IllegalArgumentException("This registration already exists"); 092 } 093 } 094 095 registrations.add(registration); 096 097 registrations = incomingByPlugin.get(registration.getPlugin()); 098 099 if (registrations == null) { 100 registrations = new HashSet<PluginMessageListenerRegistration>(); 101 incomingByPlugin.put(registration.getPlugin(), registrations); 102 } else { 103 if (registrations.contains(registration)) { 104 throw new IllegalArgumentException("This registration already exists"); 105 } 106 } 107 108 registrations.add(registration); 109 } 110 } 111 112 private void removeFromIncoming(PluginMessageListenerRegistration registration) { 113 synchronized (incomingLock) { 114 Set<PluginMessageListenerRegistration> registrations = incomingByChannel.get(registration.getChannel()); 115 116 if (registrations != null) { 117 registrations.remove(registration); 118 119 if (registrations.isEmpty()) { 120 incomingByChannel.remove(registration.getChannel()); 121 } 122 } 123 124 registrations = incomingByPlugin.get(registration.getPlugin()); 125 126 if (registrations != null) { 127 registrations.remove(registration); 128 129 if (registrations.isEmpty()) { 130 incomingByPlugin.remove(registration.getPlugin()); 131 } 132 } 133 } 134 } 135 136 private void removeFromIncoming(Plugin plugin, String channel) { 137 synchronized (incomingLock) { 138 Set<PluginMessageListenerRegistration> registrations = incomingByPlugin.get(plugin); 139 140 if (registrations != null) { 141 PluginMessageListenerRegistration[] toRemove = registrations.toArray(new PluginMessageListenerRegistration[0]); 142 143 for (PluginMessageListenerRegistration registration : toRemove) { 144 if (registration.getChannel().equals(channel)) { 145 removeFromIncoming(registration); 146 } 147 } 148 } 149 } 150 } 151 152 private void removeFromIncoming(Plugin plugin) { 153 synchronized (incomingLock) { 154 Set<PluginMessageListenerRegistration> registrations = incomingByPlugin.get(plugin); 155 156 if (registrations != null) { 157 PluginMessageListenerRegistration[] toRemove = registrations.toArray(new PluginMessageListenerRegistration[0]); 158 159 incomingByPlugin.remove(plugin); 160 161 for (PluginMessageListenerRegistration registration : toRemove) { 162 removeFromIncoming(registration); 163 } 164 } 165 } 166 } 167 168 public boolean isReservedChannel(String channel) { 169 validateChannel(channel); 170 171 return channel.equals("REGISTER") || channel.equals("UNREGISTER"); 172 } 173 174 public void registerOutgoingPluginChannel(Plugin plugin, String channel) { 175 if (plugin == null) { 176 throw new IllegalArgumentException("Plugin cannot be null"); 177 } 178 validateChannel(channel); 179 if (isReservedChannel(channel)) { 180 throw new ReservedChannelException(channel); 181 } 182 183 addToOutgoing(plugin, channel); 184 } 185 186 public void unregisterOutgoingPluginChannel(Plugin plugin, String channel) { 187 if (plugin == null) { 188 throw new IllegalArgumentException("Plugin cannot be null"); 189 } 190 validateChannel(channel); 191 192 removeFromOutgoing(plugin, channel); 193 } 194 195 public void unregisterOutgoingPluginChannel(Plugin plugin) { 196 if (plugin == null) { 197 throw new IllegalArgumentException("Plugin cannot be null"); 198 } 199 200 removeFromOutgoing(plugin); 201 } 202 203 public PluginMessageListenerRegistration registerIncomingPluginChannel(Plugin plugin, String channel, PluginMessageListener listener) { 204 if (plugin == null) { 205 throw new IllegalArgumentException("Plugin cannot be null"); 206 } 207 validateChannel(channel); 208 if (isReservedChannel(channel)) { 209 throw new ReservedChannelException(channel); 210 } 211 if (listener == null) { 212 throw new IllegalArgumentException("Listener cannot be null"); 213 } 214 215 PluginMessageListenerRegistration result = new PluginMessageListenerRegistration(this, plugin, channel, listener); 216 217 addToIncoming(result); 218 219 return result; 220 } 221 222 public void unregisterIncomingPluginChannel(Plugin plugin, String channel, PluginMessageListener listener) { 223 if (plugin == null) { 224 throw new IllegalArgumentException("Plugin cannot be null"); 225 } 226 if (listener == null) { 227 throw new IllegalArgumentException("Listener cannot be null"); 228 } 229 validateChannel(channel); 230 231 removeFromIncoming(new PluginMessageListenerRegistration(this, plugin, channel, listener)); 232 } 233 234 public void unregisterIncomingPluginChannel(Plugin plugin, String channel) { 235 if (plugin == null) { 236 throw new IllegalArgumentException("Plugin cannot be null"); 237 } 238 validateChannel(channel); 239 240 removeFromIncoming(plugin, channel); 241 } 242 243 public void unregisterIncomingPluginChannel(Plugin plugin) { 244 if (plugin == null) { 245 throw new IllegalArgumentException("Plugin cannot be null"); 246 } 247 248 removeFromIncoming(plugin); 249 } 250 251 public Set<String> getOutgoingChannels() { 252 synchronized (outgoingLock) { 253 Set<String> keys = outgoingByChannel.keySet(); 254 return ImmutableSet.copyOf(keys); 255 } 256 } 257 258 public Set<String> getOutgoingChannels(Plugin plugin) { 259 if (plugin == null) { 260 throw new IllegalArgumentException("Plugin cannot be null"); 261 } 262 263 synchronized (outgoingLock) { 264 Set<String> channels = outgoingByPlugin.get(plugin); 265 266 if (channels != null) { 267 return ImmutableSet.copyOf(channels); 268 } else { 269 return ImmutableSet.of(); 270 } 271 } 272 } 273 274 public Set<String> getIncomingChannels() { 275 synchronized (incomingLock) { 276 Set<String> keys = incomingByChannel.keySet(); 277 return ImmutableSet.copyOf(keys); 278 } 279 } 280 281 public Set<String> getIncomingChannels(Plugin plugin) { 282 if (plugin == null) { 283 throw new IllegalArgumentException("Plugin cannot be null"); 284 } 285 286 synchronized (incomingLock) { 287 Set<PluginMessageListenerRegistration> registrations = incomingByPlugin.get(plugin); 288 289 if (registrations != null) { 290 Builder<String> builder = ImmutableSet.builder(); 291 292 for (PluginMessageListenerRegistration registration : registrations) { 293 builder.add(registration.getChannel()); 294 } 295 296 return builder.build(); 297 } else { 298 return ImmutableSet.of(); 299 } 300 } 301 } 302 303 public Set<PluginMessageListenerRegistration> getIncomingChannelRegistrations(Plugin plugin) { 304 if (plugin == null) { 305 throw new IllegalArgumentException("Plugin cannot be null"); 306 } 307 308 synchronized (incomingLock) { 309 Set<PluginMessageListenerRegistration> registrations = incomingByPlugin.get(plugin); 310 311 if (registrations != null) { 312 return ImmutableSet.copyOf(registrations); 313 } else { 314 return ImmutableSet.of(); 315 } 316 } 317 } 318 319 public Set<PluginMessageListenerRegistration> getIncomingChannelRegistrations(String channel) { 320 validateChannel(channel); 321 322 synchronized (incomingLock) { 323 Set<PluginMessageListenerRegistration> registrations = incomingByChannel.get(channel); 324 325 if (registrations != null) { 326 return ImmutableSet.copyOf(registrations); 327 } else { 328 return ImmutableSet.of(); 329 } 330 } 331 } 332 333 public Set<PluginMessageListenerRegistration> getIncomingChannelRegistrations(Plugin plugin, String channel) { 334 if (plugin == null) { 335 throw new IllegalArgumentException("Plugin cannot be null"); 336 } 337 validateChannel(channel); 338 339 synchronized (incomingLock) { 340 Set<PluginMessageListenerRegistration> registrations = incomingByPlugin.get(plugin); 341 342 if (registrations != null) { 343 Builder<PluginMessageListenerRegistration> builder = ImmutableSet.builder(); 344 345 for (PluginMessageListenerRegistration registration : registrations) { 346 if (registration.getChannel().equals(channel)) { 347 builder.add(registration); 348 } 349 } 350 351 return builder.build(); 352 } else { 353 return ImmutableSet.of(); 354 } 355 } 356 } 357 358 public boolean isRegistrationValid(PluginMessageListenerRegistration registration) { 359 if (registration == null) { 360 throw new IllegalArgumentException("Registration cannot be null"); 361 } 362 363 synchronized (incomingLock) { 364 Set<PluginMessageListenerRegistration> registrations = incomingByPlugin.get(registration.getPlugin()); 365 366 if (registrations != null) { 367 return registrations.contains(registration); 368 } 369 370 return false; 371 } 372 } 373 374 public boolean isIncomingChannelRegistered(Plugin plugin, String channel) { 375 if (plugin == null) { 376 throw new IllegalArgumentException("Plugin cannot be null"); 377 } 378 validateChannel(channel); 379 380 synchronized (incomingLock) { 381 Set<PluginMessageListenerRegistration> registrations = incomingByPlugin.get(plugin); 382 383 if (registrations != null) { 384 for (PluginMessageListenerRegistration registration : registrations) { 385 if (registration.getChannel().equals(channel)) { 386 return true; 387 } 388 } 389 } 390 391 return false; 392 } 393 } 394 395 public boolean isOutgoingChannelRegistered(Plugin plugin, String channel) { 396 if (plugin == null) { 397 throw new IllegalArgumentException("Plugin cannot be null"); 398 } 399 validateChannel(channel); 400 401 synchronized (outgoingLock) { 402 Set<String> channels = outgoingByPlugin.get(plugin); 403 404 if (channels != null) { 405 return channels.contains(channel); 406 } 407 408 return false; 409 } 410 } 411 412 public void dispatchIncomingMessage(Player source, String channel, byte[] message) { 413 if (source == null) { 414 throw new IllegalArgumentException("Player source cannot be null"); 415 } 416 if (message == null) { 417 throw new IllegalArgumentException("Message cannot be null"); 418 } 419 validateChannel(channel); 420 421 Set<PluginMessageListenerRegistration> registrations = getIncomingChannelRegistrations(channel); 422 423 for (PluginMessageListenerRegistration registration : registrations) { 424 registration.getListener().onPluginMessageReceived(channel, source, message); 425 } 426 } 427 428 /** 429 * Validates a Plugin Channel name. 430 * 431 * @param channel Channel name to validate. 432 */ 433 public static void validateChannel(String channel) { 434 if (channel == null) { 435 throw new IllegalArgumentException("Channel cannot be null"); 436 } 437 if (channel.length() > Messenger.MAX_CHANNEL_SIZE) { 438 throw new ChannelNameTooLongException(channel); 439 } 440 } 441 442 /** 443 * Validates the input of a Plugin Message, ensuring the arguments are all 444 * valid. 445 * 446 * @param messenger Messenger to use for validation. 447 * @param source Source plugin of the Message. 448 * @param channel Plugin Channel to send the message by. 449 * @param message Raw message payload to send. 450 * @throws IllegalArgumentException Thrown if the source plugin is 451 * disabled. 452 * @throws IllegalArgumentException Thrown if source, channel or message 453 * is null. 454 * @throws MessageTooLargeException Thrown if the message is too big. 455 * @throws ChannelNameTooLongException Thrown if the channel name is too 456 * long. 457 * @throws ChannelNotRegisteredException Thrown if the channel is not 458 * registered for this plugin. 459 */ 460 public static void validatePluginMessage(Messenger messenger, Plugin source, String channel, byte[] message) { 461 if (messenger == null) { 462 throw new IllegalArgumentException("Messenger cannot be null"); 463 } 464 if (source == null) { 465 throw new IllegalArgumentException("Plugin source cannot be null"); 466 } 467 if (!source.isEnabled()) { 468 throw new IllegalArgumentException("Plugin must be enabled to send messages"); 469 } 470 if (message == null) { 471 throw new IllegalArgumentException("Message cannot be null"); 472 } 473 if (!messenger.isOutgoingChannelRegistered(source, channel)) { 474 throw new ChannelNotRegisteredException(channel); 475 } 476 if (message.length > Messenger.MAX_MESSAGE_SIZE) { 477 throw new MessageTooLargeException(message); 478 } 479 validateChannel(channel); 480 } 481 }