Namespace.java

package emissary.core;

import com.google.common.collect.Sets;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;

/**
 * This class is used by Emissary core to manage named classes. Each registered place gets a name which includes the
 * host, port and place type. In this version of Namespace, we simply use a map of names to objects since all objects
 * will reside in one JVM.
 *
 * @author ce
 */
public class Namespace {
    // Our logger
    private static final Logger logger = LoggerFactory.getLogger(Namespace.class);

    /** We will hold registerd class names in here */
    private static final Map<String, Object> map = new ConcurrentHashMap<>();

    /**
     * Hide the creation a new instance of NameSpace
     */
    private Namespace() {}

    /**
     * Find a registered classname
     * 
     * @param arg the name of the registered item
     */
    public static Object lookup(final String arg) throws NamespaceException {

        Object obj = map.get(arg);
        logger.trace("Namespace.lookup({}) returning: {}", arg, obj);

        if (obj != null) {
            return obj;
        }

        for (final Map.Entry<String, Object> entry : map.entrySet()) {
            if (entry.getKey().endsWith("/" + arg)) {
                obj = entry.getValue();
                break;
            }
        }

        if (obj == null) {
            throw new NamespaceException("Not found: " + arg);
        }
        return obj;
    }

    /**
     * Find a set of objects of a particular registered class
     *
     * @param arg the class of the registered item
     * @return a set of objects that are of the registered class
     */
    public static <T> Set<T> lookup(Class<T> arg) throws NamespaceException {
        return lookup(arg, false);
    }

    /**
     * Find a set of objects of a particular registered class
     *
     * @param arg the class of the registered item
     * @param silent true to silence the {@link NamespaceException}, false to throw
     * @return a set of objects that are of the registered class
     */
    public static <T> Set<T> lookup(Class<T> arg, boolean silent) throws NamespaceException {
        Set<T> lookups = Sets.newHashSet();
        map.values().stream().filter(arg::isInstance).forEach(o -> lookups.add(arg.cast(o)));
        if (!silent && CollectionUtils.isEmpty(lookups)) {
            throw new NamespaceException("Not found: " + arg.getName());
        }
        return lookups;
    }

    /**
     * Test for existence of an object named by name
     * 
     * @param name name of object to look for
     * @return true if it exists
     */
    public static boolean exists(final String name) {
        if (map.containsKey(name)) {
            return true;
        }

        for (final String key : map.keySet()) {
            if (key.endsWith("/" + name)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Bind a new object into the namespace
     * 
     * @param arg the name of the object
     * @param arg2 the instance to bind
     */
    public static void bind(final String arg, final Object arg2) {
        logger.debug("Namespace.bind({},{})", arg, arg2);
        map.put(arg, arg2);
    }

    /**
     * Remove a bound object
     * 
     * @param arg the name of the object that was used when it was bound
     */
    public static void unbind(final String arg) {
        logger.debug("Namespace.unbind({})", arg);
        map.remove(arg);
    }

    /**
     * List places registered here return a copy to prevent concurrent modification errors
     * 
     * @return Set copy of the keys bound in this namespace
     */
    public static Set<String> keySet() {
        return new TreeSet<>(map.keySet());
    }

    public static void dump() {
        logger.info("dumping Namespace");
        for (String key : keySet()) {
            try {
                logger.info("Key: {} -> Value: {}", key, lookup(key));
            } catch (NamespaceException e) {
                logger.info("Couldn't find key: {}", key);
            }
        }
    }

    public static void clear() {
        for (String key : keySet()) {
            unbind(key);
        }
    }
}