Skip to content
This repository has been archived by the owner on Aug 31, 2019. It is now read-only.

Commit

Permalink
A proper mechanism for plugins to register servers dynamically
Browse files Browse the repository at this point in the history
  • Loading branch information
jedediah committed Jan 23, 2016
1 parent 0b7b5e3 commit b1f3b47
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 1 deletion.
31 changes: 31 additions & 0 deletions api/src/main/java/net/md_5/bungee/api/ProxyServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,37 @@ public static void setInstance(ProxyServer instance)
*/
public abstract Map<String, ServerInfo> getServers();

/**
* Register the given map to be used to enumerate servers, and resolve them by name.
*
* The map returned from {@link #getServers()} is a live superset view of statically
* configured servers plus all the maps registered through this method.
*
* The master map will synchronize on each sub-map while calling any method on it, or
* iterating over any of its views. The master map will also be locked when that
* happens, so it must not be accessed while holding a lock on any of its sub-maps, or a
* deadlock may occur. The master map itself is entirely safe for concurrent access.
* Its collection views are immutable snapshots.
*
* The master map does not transform keys in any way, so if case-insensitivity is
* desired, it must be implemented by the sub-map.
*
* If multiple sub-maps contain entries with the same key, all entries will be included
* in the {@link Map#entrySet} and {@link Map#values} views of the master map,
* while the {@link Map#keySet} view will de-duplicate the keys. An arbitrary entry
* will be chosen when looking up a single duplicated key.
*
* @return true if the sub-map was added, false if it was already registered.
*/
public abstract boolean addServers(Map<String, ServerInfo> servers);

/**
* Unregister the given server sub-map.
*
* @return true if the sub-map was unregistered, false if it was not registered to begin with.
*/
public abstract boolean removeServers(Map<String, ServerInfo> servers);

/**
* Gets the server info of a server.
*
Expand Down
121 changes: 121 additions & 0 deletions api/src/main/java/net/md_5/bungee/util/ConcurrentAggregateMap.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package net.md_5.bungee.util;

import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;

public class ConcurrentAggregateMap<K, V> implements Map<K, V> {

private final Set<Map<K, V>> maps = new HashSet<>();

synchronized public boolean addMap(Map<K, V> map) {
return maps.add(map);
}

synchronized public boolean removeMap(Map<K, V> map) {
return maps.remove(map);
}

@Override
synchronized public int size() {
int n = 0;
for(Map<K, V> map : maps) {
synchronized(map) {
n += map.size();
}
}
return n;
}

@Override
public boolean isEmpty() {
return size() == 0;
}

@Override
synchronized public boolean containsKey(Object key) {
for(Map<K, V> map : maps) {
synchronized(map) {
if(map.containsKey(key)) return true;
}
}
return false;
}

@Override
synchronized public boolean containsValue(Object value) {
for(Map<K, V> map : maps) {
synchronized(map) {
if(map.containsValue(value)) return true;
}
}
return false;
}

@Override
synchronized public V get(Object key) {
for(Map<K, V> map : maps) {
synchronized(map) {
if(map.containsKey(key)) return map.get(key);
}
}
return null;
}

@Override
synchronized public Set<K> keySet() {
final ImmutableSet.Builder<K> builder = ImmutableSet.builder();
for(Map<K, V> map : maps) {
synchronized(map) {
builder.addAll(map.keySet());
}
}
return builder.build();
}

@Override
synchronized public Collection<V> values() {
final ImmutableList.Builder<V> builder = ImmutableList.builder();
for(Map<K, V> map : maps) {
synchronized(map) {
builder.addAll(map.values());
}
}
return builder.build();
}

@Override
synchronized public Set<Entry<K, V>> entrySet() {
final ImmutableSet.Builder<Map.Entry<K, V>> builder = ImmutableSet.builder();
for(Map<K, V> map : maps) {
synchronized(map) {
builder.addAll(map.entrySet());
}
}
return builder.build();
}

@Override
public V put(K key, V value) {
throw new UnsupportedOperationException("This container cannot be directly modified");
}

@Override
public V remove(Object key) {
throw new UnsupportedOperationException("This container cannot be directly modified");
}

@Override
public void putAll(Map<? extends K, ? extends V> m) {
throw new UnsupportedOperationException("This container cannot be directly modified");
}

@Override
public void clear() {
throw new UnsupportedOperationException("This container cannot be directly modified");
}
}
17 changes: 16 additions & 1 deletion proxy/src/main/java/net/md_5/bungee/BungeeCord.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
Expand Down Expand Up @@ -80,6 +81,7 @@
import net.md_5.bungee.protocol.packet.PluginMessage;
import net.md_5.bungee.query.RemoteQuery;
import net.md_5.bungee.util.CaseInsensitiveMap;
import net.md_5.bungee.util.ConcurrentAggregateMap;
import org.fusesource.jansi.AnsiConsole;

/**
Expand All @@ -97,6 +99,8 @@ public class BungeeCord extends ProxyServer
*/
@Getter
public final Configuration config = new Configuration();

private final ConcurrentAggregateMap<String, ServerInfo> servers = new ConcurrentAggregateMap<>();
/**
* Localization bundle.
*/
Expand Down Expand Up @@ -248,6 +252,7 @@ public void start() throws Exception

pluginManager.loadPlugins();
config.load();
addServers(config.getServers());

registerChannel( ForgeConstants.FML_TAG );
registerChannel( ForgeConstants.FML_HANDSHAKE_TAG );
Expand Down Expand Up @@ -536,7 +541,7 @@ public ProxiedPlayer getPlayer(UUID uuid)
@Override
public Map<String, ServerInfo> getServers()
{
return config.getServers();
return servers;
}

@Override
Expand All @@ -545,6 +550,16 @@ public ServerInfo getServerInfo(String name)
return getServers().get( name );
}

@Override
public boolean addServers(Map<String, ServerInfo> servers) {
return this.servers.addMap(servers);
}

@Override
public boolean removeServers(Map<String, ServerInfo> servers) {
return this.servers.removeMap(servers);
}

@Override
@Synchronized("pluginChannels")
public void registerChannel(String channel)
Expand Down

0 comments on commit b1f3b47

Please sign in to comment.