All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.tangosol.internal.net.topic.impl.paged.PagedTopicCaches Maven / Gradle / Ivy

There is a newer version: 24.03
Show newest version
/*
 * Copyright (c) 2000, 2021, Oracle and/or its affiliates.
 *
 * Licensed under the Universal Permissive License v 1.0 as shown at
 * http://oss.oracle.com/licenses/upl.
 */
package com.tangosol.internal.net.topic.impl.paged;

import com.tangosol.internal.net.topic.impl.paged.model.NotificationKey;
import com.tangosol.internal.net.topic.impl.paged.model.Page;
import com.tangosol.internal.net.topic.impl.paged.model.Position;
import com.tangosol.internal.net.topic.impl.paged.model.Subscription;
import com.tangosol.internal.net.topic.impl.paged.model.Usage;

import com.tangosol.internal.util.Primes;

import com.tangosol.io.ClassLoaderAware;
import com.tangosol.io.Serializer;

import com.tangosol.net.CacheService;
import com.tangosol.net.NamedCache;
import com.tangosol.net.PartitionedService;
import com.tangosol.net.cache.TypeAssertion;
import com.tangosol.net.topic.NamedTopic;

import com.tangosol.util.HashHelper;

import java.io.Closeable;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import java.util.concurrent.ThreadLocalRandom;

import java.util.function.BiFunction;
import java.util.function.Consumer;

import static com.tangosol.net.cache.TypeAssertion.withTypes;

/**
 * This class encapsulates operations on the set of {@link NamedCache}s
 * that are used to hold the underlying data for a topic.
 *
 * @author jk 2015.06.19
 * @since Coherence 14.1.1
 */
public class PagedTopicCaches
    implements Closeable, ClassLoaderAware
    {
    // ----- constructors ---------------------------------------------------

    /**
     * Create a {@link PagedTopicCaches}.
     *
     * @param sName         the name of the topic
     * @param cacheService  the {@link CacheService} owning the underlying caches
     */
    public PagedTopicCaches(String sName, CacheService cacheService)
        {
        this(sName, cacheService, null);
        }

    /**
     * Create a {@link PagedTopicCaches}.
     *
     * @param sName          the name of the topic
     * @param cacheService   the {@link CacheService} owning the underlying caches
     * @param functionCache  the function to invoke to obtain each underlying cache
     */
    public PagedTopicCaches(String sName, CacheService cacheService,
                            BiFunction functionCache)
        {
        if (sName == null || sName.isEmpty())
            {
            throw new IllegalArgumentException("The name argument cannot be null or empty String");
            }

        if (cacheService == null)
            {
            throw new IllegalArgumentException("The cacheService argument cannot be null");
            }

        if (functionCache == null)
            {
            functionCache = cacheService::ensureCache;
            }

        f_sTopicName   = sName;
        f_cacheService = cacheService;

        Pages         = functionCache.apply(Names.PAGES.cacheNameForTopicName(sName), f_cacheService.getContextClassLoader());
        Data          = functionCache.apply(Names.CONTENT.cacheNameForTopicName(sName), f_cacheService.getContextClassLoader());
        Subscriptions = functionCache.apply(Names.SUBSCRIPTIONS.cacheNameForTopicName(sName), f_cacheService.getContextClassLoader());
        Notifications = functionCache.apply(Names.NOTIFICATIONS.cacheNameForTopicName(sName), f_cacheService.getContextClassLoader());
        Usages = functionCache.apply(Names.USAGE.cacheNameForTopicName(sName), f_cacheService.getContextClassLoader());

        Set setCaches = f_setCaches = new HashSet<>();

        setCaches.add(Pages);
        setCaches.add(Data);
        setCaches.add(Subscriptions);
        setCaches.add(Notifications);
        setCaches.add(Usages);
        }

    // ----- TopicCaches methods --------------------------------------

    /**
     * Return the serializer.
     *
     * @return the serializer
     */
    public Serializer getSerializer()
        {
        return f_cacheService.getSerializer();
        }

    /**
     * Destory the PagedTopicCaches.
     */
    public void destroy()
        {
        close(/* destroy */ true);
        }

    /**
     * Return whether or not the caches are active,
     * specifically the page cache for the topic.
     *
     * @return true if the caches are active; false otherwise
     */
    public boolean isActive()
        {
        return Pages.isActive();
        }

    /**
     * Return whether or not the caches are destroyed,
     * specifically the page cache for the topic.
     *
     * @return true if the caches are destroyed; false otherwise
     */
    public boolean isDestroyed()
        {
        return Pages.isDestroyed();
        }

    /**
     * Return whether or not the caches are released,
     * specifically the page cache for the topic.
     *
     * @return true if the caches are released; false otherwise
     */
    public boolean isReleased()
        {
        return Pages.isReleased();
        }

    // ----- Closeable methods ----------------------------------------------

    @Override
    public void close()
        {
        close(/* destroy */ false);
        }

    // ----- ClassLoaderAware methods ---------------------------------------

    @Override
    public ClassLoader getContextClassLoader()
        {
        return f_cacheService.getContextClassLoader();
        }

    @Override
    public void setContextClassLoader(ClassLoader classLoader)
        {
        throw new UnsupportedOperationException();
        }

    // ----- accessor methods -----------------------------------------------

    /**
     * Return the topic name.
     *
     * @return the topic name
     */
    public String getTopicName()
        {
        return f_sTopicName;
        }

    /**
     * Get the start page for this topic upon creation.
     *
     * @return the start page
     */
    public int getBasePage()
        {
        return Math.abs(f_sTopicName.hashCode() % getPartitionCount());
        }

    /**
     * Return the partition count for this topic.
     *
     * @return the partition count for this topic
     */
    public int getPartitionCount()
        {
        return ((PartitionedService) f_cacheService).getPartitionCount();
        }

    /**
     * Return the channel count for this topic.
     *
     * @return the channel count for this topic
     */
    public int getChannelCount()
        {
        return getChannelCount(getPartitionCount());
        }

    /**
     * Compute the channel count based on the supplied partition count.
     *
     * @param cPartitions the partition count
     *
     * @return the channel count based on the supplied partition count
     */
    public static int getChannelCount(int cPartitions)
        {
        return Math.min(cPartitions, Primes.next((int) Math.sqrt(cPartitions)));
        }

    /**
     * Generate a new non-zero NotifierId.
     *
     * @return the NotifierId
     */
    public int newNotifierId()
        {
        // avoid 0 as it is used to indicate that notification is disabled (on the publisher)
        return 1 + ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE);
        }

    /**
     * Return the set of NotificationKeys covering all partitions for the given notifier
     *
     * @param nNotifier  the notifier id
     *
     * @return the NotificationKeys
     */
    public Set getPartitionNotifierSet(int nNotifier)
        {
        Set setKey = new HashSet<>();
        for (int i = 0, c = getPartitionCount(); i < c; ++i)
            {
            setKey.add(new NotificationKey(i, nNotifier));
            }

        return setKey;
        }

    /**
     * Return the unit of order for a topic partition.
     *
     * @param nPartition  the partition
     *
     * @return the unit of order
     */
    public int getUnitOfOrder(int nPartition)
        {
        return f_sTopicName.hashCode() + nPartition;
        }

    /**
     * Return the associated CacheService.
     *
     * @return the cache service
     */
    public CacheService getCacheService()
        {
        return f_cacheService;
        }

    /**
     * Return the Configuration.
     *
     * @return the configuration
     */
    public Configuration getConfiguration()
        {
        return f_cacheService.getResourceRegistry()
            .getResource(Configuration.class, getTopicName());
        }

    // ----- object methods -------------------------------------------------

    @Override
    public boolean equals(Object o)
        {
        if (this == o)
            {
            return true;
            }
        if (o == null || getClass() != o.getClass())
            {
            return false;
            }

        PagedTopicCaches that = (PagedTopicCaches) o;

        return f_sTopicName.equals(that.f_sTopicName) && f_cacheService.equals(that.f_cacheService);
        }

    @Override
    public int hashCode()
        {
        return HashHelper.hash(f_sTopicName, 31);
        }

    @Override
    public String toString()
        {
        return "TopicCaches(name='" + f_sTopicName + ")";
        }

    // ----- helper methods -------------------------------------------------

    /**
     * Close the PagedTopicCaches.
     *
     * @param fDestroy  true to destroy, false to release
     */
    private void close(boolean fDestroy)
        {
        Consumer function = fDestroy ? NamedCache::destroy : NamedCache::release;

        if (f_setCaches != null)
            {
            synchronized (this)
                {
                if (f_setCaches != null)
                    {
                    f_setCaches.stream().forEach(function);
                    f_setCaches = null;
                    }
                }
            }
        }

    // ----- data members ---------------------------------------------------

    /**
     * The topic name.
     */
    protected final String f_sTopicName;

    /**
     * The cache service.
     */
    protected final CacheService f_cacheService;

    /**
     * The caches which back the topic.
     */
    protected Set f_setCaches;

    /**
     * The cache that holds the topic pages.
     */
    public final NamedCache Pages;

    /**
     * The cache that holds the topic elements.
     */
    public final NamedCache Data;

    /**
     * The cache that holds the topic subscriber partitions.
     */
    public final NamedCache Subscriptions;

    /**
     * The cache that is used to notify blocked publishers and subscribers that they topic is no longer full/empty.
     */
    public final NamedCache Notifications;

    /**
     * The cache that holds the highest used topic pages for a cache partition.
     */
    public final NamedCache Usages;

    // ----- inner class: Names ---------------------------------------------

    /**
     * An pseudo enumeration representing the different caches used
     * by topic and topic implementations.
     *
     * @author jk 2015.06.08
     * @since Coherence 14.1.1
     */
    public static class Names
        {
        // ----- constructors ---------------------------------------------------

        /**
         * Create a TopicCacheNames with the given cache name prefix.
         *
         * @param sName       the name of this TopicCacheNames.
         * @param sPrefix     the prefix to use to obtain the cache name from the topic name
         * @param classKey    the type of the cache keys
         * @param classValue  the type of the cache values
         */
        private Names(String sName, String sPrefix, Class classKey, Class classValue, Storage storage)
            {
            f_sName         = sName;
            f_sPrefix       = sPrefix;
            f_classKey      = classKey;
            f_classValue    = classValue;
            f_typeAssertion = withTypes(f_classKey, f_classValue);
            f_storage       = storage;

            s_setValues.add(this);
            }

        // ----- TopicCacheNames methods ----------------------------------------

        /**
         * Return the cache name from the specified topic name.
         *
         * @param sTopicName  the topic name
         *
         * @return the cache name
         */
        public String cacheNameForTopicName(String sTopicName)
            {
            return f_sPrefix + sTopicName;
            }

        /**
         * Return the {@link Names} that matches the specified cache name.
         *
         * @param sCacheName  the cache name;
         *
         * @return the {@link Names} that matches the specified cache name
         */
        public static Names fromCacheName(String sCacheName)
            {
            for (Names pagedTopicCacheNames : values())
                {
                if (sCacheName.startsWith(pagedTopicCacheNames.f_sPrefix))
                    {
                    return pagedTopicCacheNames;
                    }
                }

            throw new IllegalArgumentException("Cache name " + sCacheName + " is not a valid TopicCacheName");
            }

        /**
         * For a given cache name return the topic name.
         *
         * @param sCacheName  the cache name
         *
         * @return the topic name
         */
        public static String getTopicName(String sCacheName)
            {
            for (Names pagedTopicCacheNames : values())
                {
                if (sCacheName.startsWith(pagedTopicCacheNames.f_sPrefix))
                    {
                    return sCacheName.substring(pagedTopicCacheNames.f_sPrefix.length());
                    }
                }

            return sCacheName;
            }

        /**
         * Obtain the set of all possible {@link Names}.
         *
         * @return the set of all possible {@link Names}
         */
        public static Set values()
            {
            return Collections.unmodifiableSet(s_setValues);
            }

        // ----- accessor methods -----------------------------------------------

        /**
         * Obtain the {@link TypeAssertion} to use when obtaining a type safe
         * version of the cache this {@link Names} represents.
         *
         * @return the {@link TypeAssertion} to use when obtaining a type safe
         *         version of the cache this {@link Names} represents
         */
        public TypeAssertion getTypeAssertion()
            {
            return f_typeAssertion;
            }

        /**
         * Obtain the prefix used to get the cache name from the
         * topic name.
         *
         * @return the prefix used to get the cache name from the
         *         topic name
         */
        public String getPrefix()
            {
            return f_sPrefix;
            }

        /**
         * Obtain the Class of the cache keys.
         *
         * @return the Class of the cache keys
         */
        public Class getKeyClass()
            {
            return f_classKey;
            }

        /**
         * Obtain the Class of the cache values.
         *
         * @return the Class of the cache values
         */
        public Class getValueClass()
            {
            return f_classValue;
            }

        /**
         * Obtain the storage location for the cache of this type.
         *
         * @return the storage location for the cache of this type
         */
        public Storage getStorage()
            {
            return f_storage;
            }

        /**
         * Return true if corresponding cache should be considered an internal submapping.
         *
         * @return true if corresponding sub cache mapping should be considered internal.
         */
        public boolean isInternal()
            {
            return Storage.MetaData.equals(getStorage());
            }

        // ----- object methods -------------------------------------------------

        @Override
        public boolean equals(Object o)
            {
            if (this == o)
                {
                return true;
                }
            if (o == null || getClass() != o.getClass())
                {
                return false;
                }

            Names names = (Names) o;

            return f_sName.equals(names.f_sName);

            }

        @Override
        public int hashCode()
            {
            return f_sName.hashCode();
            }

        @Override
        public String toString()
            {
            return f_sName;
            }


        // ----- constants ------------------------------------------------------

        /**
         * The {@link Set} of all legal {@link Names}.
         */
        private static final Set s_setValues = new HashSet<>();

        /**
         * The name prefix for all meta-data {@link Names} used to implement a {@link NamedTopic}.
         */
        public static final String METACACHE_PREFIX="$meta$topic";

        /**
         * The cache that holds the topic content.
         *
         * Use of no prefix rather than METACACHE_PREFIX since it is being used to filter out internal topic meta caches.
         */
        public static final Names CONTENT =
            new Names<>("content", "$topic$", Position.class, Object.class, Names.Storage.Data);

        /**
         * The cache that holds the topic pages.
         */
        public static final Names PAGES =
            new Names<>("pages", METACACHE_PREFIX +"$pages$", Page.Key.class,
                Page.class, Names.Storage.MetaData);

        /**
         * The cache that holds the topic subscriber partitions.
         */
        public static final Names SUBSCRIPTIONS =
            new Names<>("subscriptions", METACACHE_PREFIX + "$subscriptions$",
                Subscription.Key.class, Subscription.class,
                Names.Storage.MetaData);

        /**
         * The cache used for notifying publishers and subscribers of full/empty events
         */
        public static final Names NOTIFICATIONS =
            new Names<>("notifications", METACACHE_PREFIX + "$notifications$",
                NotificationKey.class, int[].class, Names.Storage.MetaData);

        /**
         * The cache that holds usage data for a cache partition.
         */
        public static final Names USAGE =
            new Names<>("usage", METACACHE_PREFIX + "$usage$",
                Usage.Key.class, Usage.class, Names.Storage.Data);

        // ----- inner class: StorageType ---------------------------------------

        public enum Storage {Data, MetaData}

        // ----- data members ---------------------------------------------------

        /**
         * The name of this {@link Names}.
         */
        private final String f_sName;

        /**
         * The prefix to add to the topic name to obtain the cache name.
         */
        private final String f_sPrefix;

        /**
         * The key class of the cache.
         */
        private final Class f_classKey;

        /**
         * The value class of the cache.
         */
        private final Class f_classValue;

        /**
         * The {@link TypeAssertion} to use when obtaining the cache.
         */
        private final TypeAssertion f_typeAssertion;

        /**
         * The storage location for the cache data.
         */
        private final Storage f_storage;
        }
    }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy