Add preliminary custom flags support.

This commit is contained in:
sk89q 2015-01-17 01:30:58 -05:00 committed by wizjany
parent 778ec404ff
commit a25674e9a5
30 changed files with 600 additions and 170 deletions

View File

@ -85,7 +85,7 @@ public class RegionContainer {
*/
void initialize() {
ConfigurationManager config = plugin.getGlobalStateManager();
container = new RegionContainerImpl(config.selectedRegionStoreDriver);
container = new RegionContainerImpl(config.selectedRegionStoreDriver, plugin.getFlagRegistry());
// Migrate to UUIDs
autoMigrate();
@ -293,7 +293,7 @@ private void autoMigrate() {
if (config.migrateRegionsToUuid) {
RegionDriver driver = getDriver();
UUIDMigration migrator = new UUIDMigration(driver, plugin.getProfileService());
UUIDMigration migrator = new UUIDMigration(driver, plugin.getProfileService(), plugin.getFlagRegistry());
migrator.setKeepUnresolvedNames(config.keepUnresolvedNames);
try {
migrate(migrator);

View File

@ -37,10 +37,33 @@
import com.sk89q.worldguard.bukkit.commands.ProtectionCommands;
import com.sk89q.worldguard.bukkit.commands.ToggleCommands;
import com.sk89q.worldguard.bukkit.event.player.ProcessPlayerEvent;
import com.sk89q.worldguard.bukkit.listener.*;
import com.sk89q.worldguard.bukkit.listener.BlacklistListener;
import com.sk89q.worldguard.bukkit.listener.BlockedPotionsListener;
import com.sk89q.worldguard.bukkit.listener.BuildPermissionListener;
import com.sk89q.worldguard.bukkit.listener.ChestProtectionListener;
import com.sk89q.worldguard.bukkit.listener.DebuggingListener;
import com.sk89q.worldguard.bukkit.listener.EventAbstractionListener;
import com.sk89q.worldguard.bukkit.listener.InvincibilityListener;
import com.sk89q.worldguard.bukkit.listener.PlayerModesListener;
import com.sk89q.worldguard.bukkit.listener.PlayerMoveListener;
import com.sk89q.worldguard.bukkit.listener.RegionFlagsListener;
import com.sk89q.worldguard.bukkit.listener.RegionProtectionListener;
import com.sk89q.worldguard.bukkit.listener.WorldGuardBlockListener;
import com.sk89q.worldguard.bukkit.listener.WorldGuardCommandBookListener;
import com.sk89q.worldguard.bukkit.listener.WorldGuardEntityListener;
import com.sk89q.worldguard.bukkit.listener.WorldGuardHangingListener;
import com.sk89q.worldguard.bukkit.listener.WorldGuardPlayerListener;
import com.sk89q.worldguard.bukkit.listener.WorldGuardServerListener;
import com.sk89q.worldguard.bukkit.listener.WorldGuardVehicleListener;
import com.sk89q.worldguard.bukkit.listener.WorldGuardWeatherListener;
import com.sk89q.worldguard.bukkit.listener.WorldGuardWorldListener;
import com.sk89q.worldguard.bukkit.listener.WorldRulesListener;
import com.sk89q.worldguard.bukkit.util.Events;
import com.sk89q.worldguard.protection.GlobalRegionManager;
import com.sk89q.worldguard.protection.flags.DefaultFlag;
import com.sk89q.worldguard.protection.flags.Flag;
import com.sk89q.worldguard.protection.flags.registry.FlagRegistry;
import com.sk89q.worldguard.protection.flags.registry.SimpleFlagRegistry;
import com.sk89q.worldguard.protection.managers.RegionManager;
import com.sk89q.worldguard.protection.managers.storage.StorageException;
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
@ -68,8 +91,17 @@
import org.bukkit.plugin.java.JavaPlugin;
import javax.annotation.Nullable;
import java.io.*;
import java.util.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.RejectedExecutionException;
@ -91,6 +123,7 @@ public class WorldGuardPlugin extends JavaPlugin {
private final ConfigurationManager configuration = new ConfigurationManager(this);
private final RegionContainer regionContainer = new RegionContainer(this);
private final GlobalRegionManager globalRegionManager = new GlobalRegionManager(this, regionContainer);
private final SimpleFlagRegistry flagRegistry = new SimpleFlagRegistry();
private SessionManager sessionManager;
private final Supervisor supervisor = new SimpleSupervisor();
private ListeningExecutorService executorService;
@ -110,6 +143,7 @@ public boolean hasPermission(CommandSender player, String perm) {
return plugin.hasPermission(player, perm);
}
};
flagRegistry.registerAll(DefaultFlag.getDefaultFlags());
}
/**
@ -127,6 +161,7 @@ public static WorldGuardPlugin inst() {
@SuppressWarnings("deprecation")
public void onEnable() {
configureLogger();
flagRegistry.setInitialized(true);
getDataFolder().mkdirs(); // Need to create the plugins/WorldGuard folder
@ -388,6 +423,15 @@ public ProfileCache getProfileCache() {
return profileCache;
}
/**
* Get the flag registry.
*
* @return the flag registry
*/
public FlagRegistry getFlagRegistry() {
return flagRegistry;
}
/**
* Check whether a player is in a group.
* This calls the corresponding method in PermissionsResolverManager

View File

@ -49,6 +49,7 @@
import com.sk89q.worldguard.protection.flags.InvalidFlagFormat;
import com.sk89q.worldguard.protection.flags.RegionGroup;
import com.sk89q.worldguard.protection.flags.RegionGroupFlag;
import com.sk89q.worldguard.protection.flags.registry.FlagRegistry;
import com.sk89q.worldguard.protection.managers.RegionManager;
import com.sk89q.worldguard.protection.managers.RemovalStrategy;
import com.sk89q.worldguard.protection.managers.migration.DriverMigration;
@ -466,6 +467,7 @@ public void flag(CommandContext args, CommandSender sender) throws CommandExcept
String flagName = args.getString(1);
String value = args.argsLength() >= 3 ? args.getJoinedStrings(2) : null;
RegionGroup groupValue = null;
FlagRegistry flagRegistry = plugin.getFlagRegistry();
RegionPermissionModel permModel = getPermissionModel(sender);
if (args.hasFlag('e')) {
@ -490,7 +492,7 @@ public void flag(CommandContext args, CommandSender sender) throws CommandExcept
throw new CommandPermissionsException();
}
Flag<?> foundFlag = DefaultFlag.fuzzyMatchFlag(flagName);
Flag<?> foundFlag = DefaultFlag.fuzzyMatchFlag(flagRegistry, flagName);
// We didn't find the flag, so let's print a list of flags that the user
// can use, and do nothing afterwards
@ -498,7 +500,7 @@ public void flag(CommandContext args, CommandSender sender) throws CommandExcept
StringBuilder list = new StringBuilder();
// Need to build a list
for (Flag<?> flag : DefaultFlag.getFlags()) {
for (Flag<?> flag : flagRegistry) {
// Can the user set this flag?
if (!permModel.maySetFlag(existing, flag)) {
continue;
@ -913,7 +915,7 @@ public void migrateDB(CommandContext args, CommandSender sender) throws CommandE
throw new CommandException("The driver specified as 'to' does not seem to be supported in your version of WorldGuard.");
}
DriverMigration migration = new DriverMigration(fromDriver, toDriver);
DriverMigration migration = new DriverMigration(fromDriver, toDriver, plugin.getFlagRegistry());
LoggerToChatHandler handler = null;
Logger minecraftLogger = null;
@ -972,7 +974,7 @@ public void migrateUuid(CommandContext args, CommandSender sender) throws Comman
ConfigurationManager config = plugin.getGlobalStateManager();
RegionContainer container = plugin.getRegionContainer();
RegionDriver driver = container.getDriver();
UUIDMigration migration = new UUIDMigration(driver, plugin.getProfileService());
UUIDMigration migration = new UUIDMigration(driver, plugin.getProfileService(), plugin.getFlagRegistry());
migration.setKeepUnresolvedNames(config.keepUnresolvedNames);
sender.sendMessage(ChatColor.YELLOW + "Now performing migration... this may take a while.");
container.migrate(migration);

View File

@ -19,11 +19,15 @@
package com.sk89q.worldguard.protection.flags;
import com.sk89q.worldguard.protection.flags.registry.FlagRegistry;
import org.bukkit.ChatColor;
import org.bukkit.GameMode;
import org.bukkit.WeatherType;
import org.bukkit.entity.EntityType;
import java.util.Arrays;
import java.util.List;
/**
* The flags that are used in WorldGuard.
*/
@ -161,18 +165,35 @@ public final class DefaultFlag {
private DefaultFlag() {
}
/**
* Get a list of default flags.
*
* @deprecated Use {@link FlagRegistry}
* @return An array of flags
*/
@Deprecated
public static Flag<?>[] getFlags() {
return flagsList;
}
/**
* Get a list of default flags.
*
* @return An array of flags
*/
public static List<Flag<?>> getDefaultFlags() {
return Arrays.asList(flagsList);
}
/**
* Try to match the flag with the given ID using a fuzzy name match.
*
* @param flagRegistry the flag registry
* @param id the flag ID
* @return a flag, or null
*/
public static Flag<?> fuzzyMatchFlag(String id) {
for (Flag<?> flag : DefaultFlag.getFlags()) {
public static Flag<?> fuzzyMatchFlag(FlagRegistry flagRegistry, String id) {
for (Flag<?> flag : flagRegistry) {
if (flag.getName().replace("-", "").equalsIgnoreCase(id.replace("-", ""))) {
return flag;
}
@ -180,4 +201,5 @@ public static Flag<?> fuzzyMatchFlag(String id) {
return null;
}
}

View File

@ -19,28 +19,46 @@
package com.sk89q.worldguard.protection.flags;
import com.google.common.collect.Iterators;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.sk89q.worldguard.protection.FlagValueCalculator;
import com.sk89q.worldguard.protection.flags.registry.FlagRegistry;
import org.bukkit.command.CommandSender;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.regex.Pattern;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* A flag carries extra data on a region.
*
* <p>Each flag implementation is a singleton and must be registered with a
* {@link FlagRegistry} to be useful. Flag values are stored as their
* proper data type, but they are first "loaded" by calling
* {@link #unmarshal(Object)}. On save, the objects are fed
* through {@link #marshal(Object)} and then saved.</p>
*/
public abstract class Flag<T> {
private static final Pattern VALID_NAME = Pattern.compile("^[:A-Za-z0-9\\-]{1,40}$");
private final String name;
private final RegionGroupFlag regionGroup;
/**
* Create a new flag.
*
* <p>Flag names should match the regex {@code ^[:A-Za-z0-9\-]{1,40}$}.</p>
*
* @param name The name of the flag
* @param defaultGroup The default group
* @throws IllegalArgumentException Thrown if the name is invalid
*/
public Flag(String name, @Nullable RegionGroup defaultGroup) {
protected Flag(String name, @Nullable RegionGroup defaultGroup) {
if (name != null && !isValidName(name)) {
throw new IllegalArgumentException("Invalid flag name used");
}
this.name = name;
this.regionGroup = defaultGroup != null ? new RegionGroupFlag(name + "-group", defaultGroup) : null;
}
@ -48,9 +66,12 @@ public Flag(String name, @Nullable RegionGroup defaultGroup) {
/**
* Create a new flag with {@link RegionGroup#ALL} as the default group.
*
* <p>Flag names should match the regex {@code ^[:A-Za-z0-9\-]{1,40}$}.</p>
*
* @param name The name of the flag
* @throws IllegalArgumentException Thrown if the name is invalid
*/
public Flag(String name) {
protected Flag(String name) {
this(name, RegionGroup.ALL);
}
@ -59,7 +80,7 @@ public Flag(String name) {
*
* @return The name of the flag
*/
public String getName() {
public final String getName() {
return name;
}
@ -73,13 +94,20 @@ public T getDefault() {
return null;
}
/**
* Given a list of values, choose the best one.
*
* <p>If there is no "best value" defined, then the first value should
* be returned. The default implementation returns the first value. If
* an implementation does have a strategy defined, then
* {@link #hasConflictStrategy()} should be overridden too.</p>
*
* @param values A collection of values
* @return The chosen value
*/
@Nullable
public T chooseValue(Collection<T> values) {
if (!values.isEmpty()) {
return values.iterator().next();
} else {
return null;
}
return Iterators.getNext(values.iterator(), null);
}
/**
@ -145,7 +173,7 @@ public boolean requiresSubject() {
*
* @return The region group flag
*/
public RegionGroupFlag getRegionGroupFlag() {
public final RegionGroupFlag getRegionGroupFlag() {
return regionGroup;
}
@ -184,4 +212,16 @@ public String toString() {
"name='" + name + '\'' +
'}';
}
/**
* Test whether a flag name is valid.
*
* @param name The flag name
* @return Whether the name is valid
*/
public static boolean isValidName(String name) {
checkNotNull(name, "name");
return VALID_NAME.matcher(name).matches();
}
}

View File

@ -0,0 +1,64 @@
/*
* WorldGuard, a suite of tools for Minecraft
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldGuard team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldguard.protection.flags;
import com.google.common.collect.Maps;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import static com.google.common.base.Preconditions.checkNotNull;
public final class Flags {
private static final Logger log = Logger.getLogger(Flags.class.getCanonicalName());
private Flags() {
}
/**
* Marshal a value of flag values into a map of raw values.
*
* @param values The unmarshalled flag values map
* @return The raw values map
*/
public static Map<String, Object> marshal(Map<Flag<?>, Object> values) {
checkNotNull(values, "values");
Map<String, Object> rawValues = Maps.newHashMap();
for (Entry<Flag<?>, Object> entry : values.entrySet()) {
try {
rawValues.put(entry.getKey().getName(), marshal(entry.getKey(), entry.getValue()));
} catch (Exception e) {
log.log(Level.WARNING, "Failed to marshal flag value for " + entry.getKey() + "; value is " + entry.getValue(), e);
}
}
return rawValues;
}
@SuppressWarnings("unchecked")
private static <T> Object marshal(Flag<T> flag, Object value) {
return flag.marshal((T) value);
}
}

View File

@ -0,0 +1,28 @@
/*
* WorldGuard, a suite of tools for Minecraft
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldGuard team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldguard.protection.flags.registry;
public class FlagConflictException extends RuntimeException {
public FlagConflictException(String message) {
super(message);
}
}

View File

@ -0,0 +1,85 @@
/*
* WorldGuard, a suite of tools for Minecraft
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldGuard team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldguard.protection.flags.registry;
import com.sk89q.worldguard.protection.flags.Flag;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.Map;
/**
* Keeps track of registered flags.
*/
public interface FlagRegistry extends Iterable<Flag<?>> {
/**
* Register a new flag.
*
* <p>There may be an appropriate time to register flags. If flags are
* registered outside this time, then an exception may be thrown.</p>
*
* @param flag The flag
* @throws FlagConflictException Thrown when an existing flag exists with the same name
* @throws IllegalStateException If it is not the right time to register new flags
*/
void register(Flag<?> flag) throws FlagConflictException;
/**
* Register a collection of flags.
*
* <p>There may be an appropriate time to register flags. If flags are
* registered outside this time, then an exception may be thrown.</p>
*
* <p>If there is a flag conflict, then an error will be logged but
* no exception will be thrown.</p>
*
* @param flags a collection of flags
* @throws IllegalStateException If it is not the right time to register new flags
*/
void registerAll(Collection<Flag<?>> flags);
/**
* Get af flag by its name.
*
* @param name The name
* @return The flag, if it has been registered
*/
@Nullable
Flag<?> get(String name);
/**
* Unmarshal a raw map of values into a map of flags with their
* unmarshalled values.
*
* @param rawValues The raw values map
* @param createUnknown Whether "just in time" flags should be created for unknown flags
* @return The unmarshalled flag values map
*/
Map<Flag<?>, Object> unmarshal(Map<String, Object> rawValues, boolean createUnknown);
/**
* Get the number of registered flags.
*
* @return The number of registered flags
*/
int size();
}

View File

@ -0,0 +1,148 @@
/*
* WorldGuard, a suite of tools for Minecraft
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldGuard team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldguard.protection.flags.registry;
import com.google.common.collect.Iterators;
import com.google.common.collect.Maps;
import com.sk89q.worldguard.protection.flags.Flag;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import static com.google.common.base.Preconditions.checkNotNull;
public class SimpleFlagRegistry implements FlagRegistry {
private static final Logger log = Logger.getLogger(SimpleFlagRegistry.class.getCanonicalName());
private final Object lock = new Object();
private final ConcurrentMap<String, Flag<?>> flags = Maps.newConcurrentMap();
private boolean initialized;
public boolean isInitialized() {
return initialized;
}
public void setInitialized(boolean initialized) {
this.initialized = initialized;
}
@Override
public void register(Flag<?> flag) throws FlagConflictException {
synchronized (lock) {
if (initialized) {
throw new IllegalStateException("New flags cannot be registered at this time");
}
forceRegister(flag);
}
}
@Override
public void registerAll(Collection<Flag<?>> flags) {
synchronized (lock) {
for (Flag<?> flag : flags) {
try {
register(flag);
} catch (FlagConflictException e) {
log.log(Level.WARNING, e.getMessage());
}
}
}
}
private Flag<?> forceRegister(Flag<?> flag) throws FlagConflictException {
checkNotNull(flag, "flag");
checkNotNull(flag.getName(), "flag.getName()");
synchronized (lock) {
String name = flag.getName().toLowerCase();
if (flags.containsKey(name)) {
throw new FlagConflictException("A flag already exists by the name " + name);
}
flags.put(name, flag);
}
return flag;
}
@Override
@Nullable
public Flag<?> get(String name) {
checkNotNull(name, "name");
return flags.get(name.toLowerCase());
}
private Flag<?> getOrCreate(String name) {
Flag<?> flag = get(name);
if (flag != null) {
return flag;
}
synchronized (lock) {
flag = get(name); // Load again because the previous load was not synchronized
return flag != null ? flag : forceRegister(new UnknownFlag(name));
}
}
@Override
public Map<Flag<?>, Object> unmarshal(Map<String, Object> rawValues, boolean createUnknown) {
checkNotNull(rawValues, "rawValues");
ConcurrentMap<Flag<?>, Object> values = Maps.newConcurrentMap();
for (Entry<String, Object> entry : rawValues.entrySet()) {
Flag<?> flag = createUnknown ? getOrCreate(entry.getKey()) : get(entry.getKey());
if (flag != null) {
try {
Object unmarshalled = flag.unmarshal(entry.getValue());
if (unmarshalled != null) {
values.put(flag, unmarshalled);
} else {
log.warning("Failed to parse flag '" + flag.getName() + "' with value '" + entry.getValue() + "'");
}
} catch (Exception e) {
log.log(Level.WARNING, "Failed to unmarshal flag value for " + flag, e);
}
}
}
return values;
}
@Override
public int size() {
return flags.size();
}
@Override
public Iterator<Flag<?>> iterator() {
return Iterators.unmodifiableIterator(flags.values().iterator());
}
}

View File

@ -0,0 +1,50 @@
/*
* WorldGuard, a suite of tools for Minecraft
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldGuard team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldguard.protection.flags.registry;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.sk89q.worldguard.protection.flags.Flag;
import com.sk89q.worldguard.protection.flags.InvalidFlagFormat;
import org.bukkit.command.CommandSender;
import javax.annotation.Nullable;
public class UnknownFlag extends Flag<Object> {
public UnknownFlag(String name) {
super(name);
}
@Override
public Object parseInput(WorldGuardPlugin plugin, CommandSender sender, String input) throws InvalidFlagFormat {
throw new InvalidFlagFormat("The plugin that registered this flag is not currently installed");
}
@Override
public Object unmarshal(@Nullable Object o) {
return o;
}
@Override
public Object marshal(Object o) {
return o;
}
}

View File

@ -20,6 +20,7 @@
package com.sk89q.worldguard.protection.managers;
import com.google.common.base.Supplier;
import com.sk89q.worldguard.protection.flags.registry.FlagRegistry;
import com.sk89q.worldguard.protection.managers.index.ChunkHashTable;
import com.sk89q.worldguard.protection.managers.index.ConcurrentRegionIndex;
import com.sk89q.worldguard.protection.managers.index.PriorityRTreeIndex;
@ -29,16 +30,7 @@
import com.sk89q.worldguard.util.Normal;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.WeakHashMap;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Level;
@ -62,6 +54,7 @@ public class RegionContainerImpl {
private final RegionDriver driver;
private final Supplier<? extends ConcurrentRegionIndex> indexFactory = new ChunkHashTable.Factory(new PriorityRTreeIndex.Factory());
private final Timer timer = new Timer();
private final FlagRegistry flagRegistry;
private final Set<Normal> failingLoads = new HashSet<Normal>();
private final Set<RegionManager> failingSaves = Collections.synchronizedSet(
@ -71,12 +64,15 @@ public class RegionContainerImpl {
* Create a new instance.
*
* @param driver the region store driver
* @param flagRegistry the flag registry
*/
public RegionContainerImpl(RegionDriver driver) {
public RegionContainerImpl(RegionDriver driver, FlagRegistry flagRegistry) {
checkNotNull(driver);
checkNotNull(flagRegistry, "flagRegistry");
this.driver = driver;
timer.schedule(new BackgroundLoader(), LOAD_ATTEMPT_INTERVAL, LOAD_ATTEMPT_INTERVAL);
timer.schedule(new BackgroundSaver(), SAVE_INTERVAL, SAVE_INTERVAL);
this.flagRegistry = flagRegistry;
}
/**
@ -129,7 +125,7 @@ public RegionManager load(String name) {
*/
private RegionManager createAndLoad(String name) throws StorageException {
RegionDatabase store = driver.get(name);
RegionManager manager = new RegionManager(store, indexFactory);
RegionManager manager = new RegionManager(store, indexFactory, flagRegistry);
manager.load(); // Try loading, although it may fail
return manager;
}

View File

@ -27,6 +27,7 @@
import com.sk89q.worldguard.LocalPlayer;
import com.sk89q.worldguard.protection.ApplicableRegionSet;
import com.sk89q.worldguard.protection.RegionResultSet;
import com.sk89q.worldguard.protection.flags.registry.FlagRegistry;
import com.sk89q.worldguard.protection.managers.index.ConcurrentRegionIndex;
import com.sk89q.worldguard.protection.managers.index.RegionIndex;
import com.sk89q.worldguard.protection.managers.storage.DifferenceSaveException;
@ -37,14 +38,7 @@
import com.sk89q.worldguard.util.Normal;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@ -57,6 +51,7 @@ public final class RegionManager {
private final RegionDatabase store;
private final Supplier<? extends ConcurrentRegionIndex> indexFactory;
private final FlagRegistry flagRegistry;
private ConcurrentRegionIndex index;
/**
@ -64,14 +59,17 @@ public final class RegionManager {
*
* @param store the region store
* @param indexFactory the factory for creating new instances of the index
* @param flagRegistry the flag registry
*/
public RegionManager(RegionDatabase store, Supplier<? extends ConcurrentRegionIndex> indexFactory) {
public RegionManager(RegionDatabase store, Supplier<? extends ConcurrentRegionIndex> indexFactory, FlagRegistry flagRegistry) {
checkNotNull(store);
checkNotNull(indexFactory);
checkNotNull(flagRegistry, "flagRegistry");
this.store = store;
this.indexFactory = indexFactory;
this.index = indexFactory.get();
this.flagRegistry = flagRegistry;
}
/**
@ -93,7 +91,7 @@ public String getName() {
* @throws StorageException thrown when loading fails
*/
public void load() throws StorageException {
Set<ProtectedRegion> regions = store.loadAll();
Set<ProtectedRegion> regions = store.loadAll(flagRegistry);
for (ProtectedRegion region : regions) {
region.setDirty(false);
}

View File

@ -19,6 +19,8 @@
package com.sk89q.worldguard.protection.managers.migration;
import com.google.common.base.Preconditions;
import com.sk89q.worldguard.protection.flags.registry.FlagRegistry;
import com.sk89q.worldguard.protection.managers.storage.RegionDatabase;
import com.sk89q.worldguard.protection.managers.storage.RegionDriver;
import com.sk89q.worldguard.protection.managers.storage.StorageException;
@ -36,17 +38,21 @@ public class DriverMigration extends AbstractMigration {
private static final Logger log = Logger.getLogger(DriverMigration.class.getCanonicalName());
private final RegionDriver target;
private final FlagRegistry flagRegistry;
/**
* Create a new instance.
*
* @param driver the source storage driver
* @param target the target storage driver
* @param flagRegistry the flag registry
*/
public DriverMigration(RegionDriver driver, RegionDriver target) {
public DriverMigration(RegionDriver driver, RegionDriver target, FlagRegistry flagRegistry) {
super(driver);
checkNotNull(target);
Preconditions.checkNotNull(flagRegistry, "flagRegistry");
this.target = target;
this.flagRegistry = flagRegistry;
}
@Override
@ -56,7 +62,7 @@ protected void migrate(RegionDatabase store) throws MigrationException {
log.info("Loading the regions for '" + store.getName() + "' with the old driver...");
try {
regions = store.loadAll();
regions = store.loadAll(flagRegistry);
} catch (StorageException e) {
throw new MigrationException("Failed to load region data for the world '" + store.getName() + "'", e);
}

View File

@ -24,18 +24,14 @@
import com.sk89q.squirrelid.resolver.ProfileService;
import com.sk89q.worldguard.domains.DefaultDomain;
import com.sk89q.worldguard.domains.PlayerDomain;
import com.sk89q.worldguard.protection.flags.registry.FlagRegistry;
import com.sk89q.worldguard.protection.managers.storage.RegionDatabase;
import com.sk89q.worldguard.protection.managers.storage.RegionDriver;
import com.sk89q.worldguard.protection.managers.storage.StorageException;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Level;
@ -53,6 +49,7 @@ public class UUIDMigration extends AbstractMigration {
private final Timer timer = new Timer();
private final ProfileService profileService;
private final FlagRegistry flagRegistry;
private final ConcurrentMap<String, UUID> resolvedNames = new ConcurrentHashMap<String, UUID>();
private final Set<String> unresolvedNames = new HashSet<String>();
private boolean keepUnresolvedNames = true;
@ -62,11 +59,14 @@ public class UUIDMigration extends AbstractMigration {
*
* @param driver the storage driver
* @param profileService the profile service
* @param flagRegistry the flag registry
*/
public UUIDMigration(RegionDriver driver, ProfileService profileService) {
public UUIDMigration(RegionDriver driver, ProfileService profileService, FlagRegistry flagRegistry) {
super(driver);
checkNotNull(profileService);
checkNotNull(flagRegistry, "flagRegistry");
this.profileService = profileService;
this.flagRegistry = flagRegistry;
}
@Override
@ -76,7 +76,7 @@ protected void migrate(RegionDatabase store) throws MigrationException {
Set<ProtectedRegion> regions;
try {
regions = store.loadAll();
regions = store.loadAll(flagRegistry);
} catch (StorageException e) {
throw new MigrationException("Failed to load region data for the world '" + store.getName() + "'", e);
}

View File

@ -19,6 +19,7 @@
package com.sk89q.worldguard.protection.managers.storage;
import com.sk89q.worldguard.protection.flags.registry.FlagRegistry;
import com.sk89q.worldguard.protection.managers.RegionDifference;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
@ -42,7 +43,7 @@ public String getName() {
}
@Override
public Set<ProtectedRegion> loadAll() {
public Set<ProtectedRegion> loadAll(FlagRegistry flagRegistry) {
return regions;
}

View File

@ -19,6 +19,7 @@
package com.sk89q.worldguard.protection.managers.storage;
import com.sk89q.worldguard.protection.flags.registry.FlagRegistry;
import com.sk89q.worldguard.protection.managers.RegionDifference;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
@ -53,10 +54,11 @@ public interface RegionDatabase {
* {@code get()} and {@code put()} calls in order to maximize performance.
* </p>
*
* @param flags a flag registry
* @return a set of loaded regions
* @throws StorageException thrown on read error
*/
Set<ProtectedRegion> loadAll() throws StorageException;
Set<ProtectedRegion> loadAll(FlagRegistry flags) throws StorageException;
/**
* Replace all the data in the store with the given collection of regions.

View File

@ -19,7 +19,6 @@
package com.sk89q.worldguard.protection.managers.storage;
import com.sk89q.worldguard.protection.flags.DefaultFlag;
import com.sk89q.worldguard.protection.flags.Flag;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import com.sk89q.worldguard.protection.regions.ProtectedRegion.CircularInheritanceException;
@ -43,62 +42,6 @@ public final class RegionDatabaseUtils {
private RegionDatabaseUtils() {
}
/**
* Try setting the given map of flags onto the region.
*
* @param region the region
* @param flagData the map of flag data
*/
public static void trySetFlagMap(ProtectedRegion region, Map<String, Object> flagData) {
checkNotNull(region);
checkNotNull(flagData);
for (Flag<?> flag : DefaultFlag.getFlags()) {
if (flag == null) {
// Some plugins that add custom flags to WorldGuard are doing
// something very wrong -- see WORLDGUARD-3094
continue;
}
Object o = flagData.get(flag.getName());
if (o != null) {
RegionDatabaseUtils.trySetFlag(region, flag, o);
}
// Set group
if (flag.getRegionGroupFlag() != null) {
Object o2 = flagData.get(flag.getRegionGroupFlag().getName());
if (o2 != null) {
RegionDatabaseUtils.trySetFlag(region, flag.getRegionGroupFlag(), o2);
}
}
}
}
/**
* Try to set a flag on the region.
*
* @param region the region
* @param flag the flag
* @param value the value of the flag, which may be {@code null}
* @param <T> the flag's type
* @return true if the set succeeded
*/
public static <T> boolean trySetFlag(ProtectedRegion region, Flag<T> flag, @Nullable Object value) {
checkNotNull(region);
checkNotNull(flag);
T val = flag.unmarshal(value);
if (val != null) {
region.setFlag(flag, val);
return true;
} else {
log.warning("Failed to parse flag '" + flag.getName() + "' with value '" + value + "'");
return false;
}
}
/**
* Re-link parent regions on each provided region using the two
* provided maps.

View File

@ -26,7 +26,8 @@
import com.sk89q.worldedit.BlockVector2D;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldguard.domains.DefaultDomain;
import com.sk89q.worldguard.protection.flags.Flag;
import com.sk89q.worldguard.protection.flags.Flags;
import com.sk89q.worldguard.protection.flags.registry.FlagRegistry;
import com.sk89q.worldguard.protection.managers.RegionDifference;
import com.sk89q.worldguard.protection.managers.storage.DifferenceSaveException;
import com.sk89q.worldguard.protection.managers.storage.RegionDatabase;
@ -45,15 +46,7 @@
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -97,8 +90,8 @@ public class YamlRegionFile implements RegionDatabase {
* @param file the file
*/
public YamlRegionFile(String name, File file) {
checkNotNull(name);
checkNotNull(file);
checkNotNull(name, "name");
checkNotNull(file, "file");
this.name = name;
this.file = file;
}
@ -109,7 +102,7 @@ public String getName() {
}
@Override
public Set<ProtectedRegion> loadAll() throws StorageException {
public Set<ProtectedRegion> loadAll(FlagRegistry flagRegistry) throws StorageException {
Map<String, ProtectedRegion> loaded = new HashMap<String, ProtectedRegion>();
YAMLProcessor config = createYamlProcessor(file);
@ -162,7 +155,7 @@ public Set<ProtectedRegion> loadAll() throws StorageException {
Integer priority = checkNotNull(node.getInt("priority"));
region.setPriority(priority);
setFlags(region, node.getNode("flags"));
setFlags(flagRegistry, region, node.getNode("flags"));
region.setOwners(parseDomain(node.getNode("owners")));
region.setMembers(parseDomain(node.getNode("members")));
@ -287,31 +280,15 @@ private DefaultDomain parseDomain(YAMLNode node) {
}
private Map<String, Object> getFlagData(ProtectedRegion region) {
Map<String, Object> flagData = new HashMap<String, Object>();
for (Map.Entry<Flag<?>, Object> entry : region.getFlags().entrySet()) {
Flag<?> flag = entry.getKey();
addMarshalledFlag(flagData, flag, entry.getValue());
}
return flagData;
return Flags.marshal(region.getFlags());
}
private void setFlags(ProtectedRegion region, YAMLNode flagsData) {
private void setFlags(FlagRegistry flagRegistry, ProtectedRegion region, YAMLNode flagsData) {
if (flagsData != null) {
RegionDatabaseUtils.trySetFlagMap(region, flagsData.getMap());
region.setFlags(flagRegistry.unmarshal(flagsData.getMap(), true));
}
}
@SuppressWarnings("unchecked")
private <V> void addMarshalledFlag(Map<String, Object> flagData, Flag<V> flag, Object val) {
if (val == null) {
return;
}
flagData.put(flag.getName(), flag.marshal((V) val));
}
private Map<String, Object> getDomainData(DefaultDomain domain) {
Map<String, Object> domainData = new HashMap<String, Object>();

View File

@ -27,6 +27,7 @@
import com.sk89q.worldedit.BlockVector2D;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldguard.domains.DefaultDomain;
import com.sk89q.worldguard.protection.flags.registry.FlagRegistry;
import com.sk89q.worldguard.protection.managers.storage.RegionDatabaseUtils;
import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion;
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
@ -45,6 +46,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
@ -59,17 +61,19 @@ class DataLoader {
final Connection conn;
final DataSourceConfig config;
final int worldId;
final FlagRegistry flagRegistry;
private final Map<String, ProtectedRegion> loaded = new HashMap<String, ProtectedRegion>();
private final Map<ProtectedRegion, String> parentSets = new HashMap<ProtectedRegion, String>();
private final Yaml yaml = SQLRegionDatabase.createYaml();
DataLoader(SQLRegionDatabase regionStore, Connection conn) throws SQLException {
DataLoader(SQLRegionDatabase regionStore, Connection conn, FlagRegistry flagRegistry) throws SQLException {
checkNotNull(regionStore);
this.conn = conn;
this.config = regionStore.getDataSourceConfig();
this.worldId = regionStore.getWorldId();
this.flagRegistry = flagRegistry;
}
public Set<ProtectedRegion> load() throws SQLException {
@ -239,13 +243,9 @@ private void loadFlags() throws SQLException {
unmarshalFlagValue(rs.getString("value")));
}
for (Map.Entry<String, Map<String, Object>> entry : data.rowMap().entrySet()) {
for (Entry<String, Map<String, Object>> entry : data.rowMap().entrySet()) {
ProtectedRegion region = loaded.get(entry.getKey());
if (region != null) {
RegionDatabaseUtils.trySetFlagMap(region, entry.getValue());
} else {
throw new RuntimeException("An unexpected error occurred (loaded.get() returned null)");
}
region.setFlags(flagRegistry.unmarshal(entry.getValue(), true));
}
} finally {
closer.closeQuietly();

View File

@ -19,6 +19,7 @@
package com.sk89q.worldguard.protection.managers.storage.sql;
import com.sk89q.worldguard.protection.flags.registry.FlagRegistry;
import com.sk89q.worldguard.protection.managers.RegionDifference;
import com.sk89q.worldguard.protection.managers.storage.DifferenceSaveException;
import com.sk89q.worldguard.protection.managers.storage.RegionDatabase;
@ -198,7 +199,7 @@ static Yaml createYaml() {
}
@Override
public Set<ProtectedRegion> loadAll() throws StorageException {
public Set<ProtectedRegion> loadAll(FlagRegistry flagRegistry) throws StorageException {
initialize();
Closer closer = Closer.create();
@ -206,7 +207,7 @@ public Set<ProtectedRegion> loadAll() throws StorageException {
try {
try {
loader = new DataLoader(this, closer.register(getConnection()));
loader = new DataLoader(this, closer.register(getConnection()), flagRegistry);
} catch (SQLException e) {
throw new StorageException("Failed to get a connection to the database", e);
}

View File

@ -27,7 +27,7 @@ public class HashMapIndexPriorityTest extends RegionPriorityTest {
@Override
protected RegionManager createRegionManager() throws Exception {
return new RegionManager(new MemoryRegionDatabase(), new HashMapIndex.Factory());
return new RegionManager(new MemoryRegionDatabase(), new HashMapIndex.Factory(), getFlagRegistry());
}
}

View File

@ -27,7 +27,7 @@ public class HashMapIndexRegionOverlapTest extends RegionOverlapTest {
@Override
protected RegionManager createRegionManager() throws Exception {
return new RegionManager(new MemoryRegionDatabase(), new HashMapIndex.Factory());
return new RegionManager(new MemoryRegionDatabase(), new HashMapIndex.Factory(), getFlagRegistry());
}
}

View File

@ -19,15 +19,15 @@
package com.sk89q.worldguard.protection;
import com.sk89q.worldguard.protection.managers.index.HashMapIndex;
import com.sk89q.worldguard.protection.managers.RegionManager;
import com.sk89q.worldguard.protection.managers.index.HashMapIndex;
import com.sk89q.worldguard.protection.managers.storage.MemoryRegionDatabase;
public class HashMapIndexTest extends RegionOverlapTest {
@Override
protected RegionManager createRegionManager() throws Exception {
return new RegionManager(new MemoryRegionDatabase(), new HashMapIndex.Factory());
return new RegionManager(new MemoryRegionDatabase(), new HashMapIndex.Factory(), getFlagRegistry());
}
}

View File

@ -27,7 +27,7 @@ public class PriorityRTreeIndexTest extends RegionOverlapTest {
@Override
protected RegionManager createRegionManager() throws Exception {
return new RegionManager(new MemoryRegionDatabase(), new PriorityRTreeIndex.Factory());
return new RegionManager(new MemoryRegionDatabase(), new PriorityRTreeIndex.Factory(), getFlagRegistry());
}
}

View File

@ -27,7 +27,7 @@ public class PriorityRTreeRegionEntryExitTest extends RegionEntryExitTest {
@Override
protected RegionManager createRegionManager() throws Exception {
return new RegionManager(new MemoryRegionDatabase(), new PriorityRTreeIndex.Factory());
return new RegionManager(new MemoryRegionDatabase(), new PriorityRTreeIndex.Factory(), getFlagRegistry());
}
}

View File

@ -27,7 +27,7 @@ public class PriorityRTreeRegionOverlapTest extends RegionOverlapTest {
@Override
protected RegionManager createRegionManager() throws Exception {
return new RegionManager(new MemoryRegionDatabase(), new PriorityRTreeIndex.Factory());
return new RegionManager(new MemoryRegionDatabase(), new PriorityRTreeIndex.Factory(), getFlagRegistry());
}
}

View File

@ -27,7 +27,7 @@ public class PriorityRTreeRegionPriorityTest extends RegionPriorityTest {
@Override
protected RegionManager createRegionManager() throws Exception {
return new RegionManager(new MemoryRegionDatabase(), new PriorityRTreeIndex.Factory());
return new RegionManager(new MemoryRegionDatabase(), new PriorityRTreeIndex.Factory(), getFlagRegistry());
}
}

View File

@ -26,6 +26,8 @@
import com.sk89q.worldguard.protection.flags.DefaultFlag;
import com.sk89q.worldguard.protection.flags.RegionGroup;
import com.sk89q.worldguard.protection.flags.StateFlag;
import com.sk89q.worldguard.protection.flags.registry.FlagRegistry;
import com.sk89q.worldguard.protection.flags.registry.SimpleFlagRegistry;
import com.sk89q.worldguard.protection.managers.RegionManager;
import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion;
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
@ -53,6 +55,12 @@ public abstract class RegionEntryExitTest {
TestPlayer vipPlayer;
TestPlayer builderPlayer;
protected FlagRegistry getFlagRegistry() {
FlagRegistry registry = new SimpleFlagRegistry();
registry.registerAll(DefaultFlag.getDefaultFlags());
return registry;
}
protected abstract RegionManager createRegionManager() throws Exception;
@Before

View File

@ -19,15 +19,6 @@
package com.sk89q.worldguard.protection;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.HashSet;
import org.junit.Before;
import org.junit.Test;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.BlockVector2D;
import com.sk89q.worldedit.Vector;
@ -35,11 +26,21 @@
import com.sk89q.worldguard.domains.DefaultDomain;
import com.sk89q.worldguard.protection.flags.DefaultFlag;
import com.sk89q.worldguard.protection.flags.StateFlag;
import com.sk89q.worldguard.protection.flags.registry.FlagRegistry;
import com.sk89q.worldguard.protection.flags.registry.SimpleFlagRegistry;
import com.sk89q.worldguard.protection.managers.RegionManager;
import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion;
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.HashSet;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public abstract class RegionOverlapTest {
static String COURTYARD_ID = "courtyard";
@ -58,6 +59,12 @@ public abstract class RegionOverlapTest {
ProtectedRegion fountain;
TestPlayer player1;
TestPlayer player2;
protected FlagRegistry getFlagRegistry() {
FlagRegistry registry = new SimpleFlagRegistry();
registry.registerAll(DefaultFlag.getDefaultFlags());
return registry;
}
protected abstract RegionManager createRegionManager() throws Exception;

View File

@ -26,6 +26,8 @@
import com.sk89q.worldguard.domains.DefaultDomain;
import com.sk89q.worldguard.protection.flags.DefaultFlag;
import com.sk89q.worldguard.protection.flags.StateFlag;
import com.sk89q.worldguard.protection.flags.registry.FlagRegistry;
import com.sk89q.worldguard.protection.flags.registry.SimpleFlagRegistry;
import com.sk89q.worldguard.protection.managers.RegionManager;
import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion;
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
@ -55,6 +57,12 @@ public abstract class RegionPriorityTest {
ProtectedRegion fountain;
TestPlayer player1;
TestPlayer player2;
protected FlagRegistry getFlagRegistry() {
FlagRegistry registry = new SimpleFlagRegistry();
registry.registerAll(DefaultFlag.getDefaultFlags());
return registry;
}
protected abstract RegionManager createRegionManager() throws Exception;