Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package world.bentobox.bentobox.api.metadata;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Optional;

/**
Expand Down Expand Up @@ -56,4 +57,24 @@ default Optional<MetaDataValue> removeMetaData(String key) {
return getMetaData().map(m -> m.remove(key));
}

/**
* Copies the given map into a new {@link ConcurrentHashMap}, dropping null keys and values,
* which a concurrent map cannot hold. Implementations use this to make their backing map
* safe for concurrent access without mutating the caller's map.
* @param source map to copy, may be null
* @return a mutable, thread-safe copy
* @since 3.23.0
*/
static Map<String, MetaDataValue> toConcurrentMap(Map<String, MetaDataValue> source) {
Map<String, MetaDataValue> result = new ConcurrentHashMap<>();
if (source != null) {
source.forEach((key, value) -> {
if (key != null && value != null) {
result.put(key, value);
}
});
}
return result;
}

}
25 changes: 3 additions & 22 deletions src/main/java/world/bentobox/bentobox/database/objects/Island.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -293,7 +292,7 @@ public Island(Island island) {
this.maxHomes = island.getMaxHomes();
this.maxMembers = new HashMap<>(island.getMaxMembers());
this.members.putAll(island.getMembers());
this.metaData = island.getMetaData().map(Island::toConcurrentMap).orElse(null);
this.metaData = island.getMetaData().map(MetaDataAble::toConcurrentMap).orElse(null);
this.name = island.getName();
this.owner = island.getOwner();
this.protectionRange = island.getProtectionRange();
Expand Down Expand Up @@ -1756,36 +1755,18 @@ public void setReserved(boolean reserved) {
@Override
public Optional<Map<String, MetaDataValue>> getMetaData() {
if (!(metaData instanceof ConcurrentMap)) {
metaData = toConcurrentMap(metaData);
metaData = MetaDataAble.toConcurrentMap(metaData);
}
return Optional.of(metaData);
}

/**
* Copies the given map into a new {@link ConcurrentHashMap}, dropping null keys and values,
* which a concurrent map cannot hold.
* @param source map to copy, may be null
* @return a mutable, thread-safe copy
*/
private static Map<String, MetaDataValue> toConcurrentMap(Map<String, MetaDataValue> source) {
Map<String, MetaDataValue> result = new ConcurrentHashMap<>();
if (source != null) {
source.forEach((key, value) -> {
if (key != null && value != null) {
result.put(key, value);
}
});
}
return result;
}

/**
* @param metaData the metaData to set
* @since 1.15.4
*/
@Override
public void setMetaData(Map<String, MetaDataValue> metaData) {
this.metaData = metaData == null ? null : toConcurrentMap(metaData);
this.metaData = metaData == null ? null : MetaDataAble.toConcurrentMap(metaData);
setChanged();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.bukkit.Bukkit;
Expand Down Expand Up @@ -254,7 +253,7 @@ public void addToPendingKick(World world)
* <p>
* The map may arrive as {@code null} (new player), as an immutable or Gson-created map
* (deserialization), or as a plain {@link HashMap}. All of these are copied once into a
* {@link ConcurrentHashMap}. Metadata is read by many addons on hot paths such as
* {@link java.util.concurrent.ConcurrentHashMap}. Metadata is read by many addons on hot paths such as
* {@code PlayerMoveEvent} and can be touched from other threads, so the backing map must
* tolerate concurrent access without corrupting itself.
* @return the metaData
Expand All @@ -264,29 +263,11 @@ public void addToPendingKick(World world)
@Override
public Optional<Map<String, MetaDataValue>> getMetaData() {
if (!(metaData instanceof ConcurrentMap)) {
metaData = toConcurrentMap(metaData);
metaData = MetaDataAble.toConcurrentMap(metaData);
}
return Optional.of(metaData);
}

/**
* Copies the given map into a new {@link ConcurrentHashMap}, dropping null keys and values,
* which a concurrent map cannot hold.
* @param source map to copy, may be null
* @return a mutable, thread-safe copy
*/
private static Map<String, MetaDataValue> toConcurrentMap(Map<String, MetaDataValue> source) {
Map<String, MetaDataValue> result = new ConcurrentHashMap<>();
if (source != null) {
source.forEach((key, value) -> {
if (key != null && value != null) {
result.put(key, value);
}
});
}
return result;
}

/**
* Sets the player's metadata. The map is copied into a thread-safe map, so the caller's map
* is never mutated and immutable maps are accepted.
Expand All @@ -296,7 +277,7 @@ private static Map<String, MetaDataValue> toConcurrentMap(Map<String, MetaDataVa
*/
@Override
public void setMetaData(Map<String, MetaDataValue> metaData) {
this.metaData = metaData == null ? null : toConcurrentMap(metaData);
this.metaData = metaData == null ? null : MetaDataAble.toConcurrentMap(metaData);
}

/**
Expand Down
Loading