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

com.alachisoft.ncache.client.internal.messaging.MessageManager Maven / Gradle / Ivy

package com.alachisoft.ncache.client.internal.messaging;

import Alachisoft.NCache.Common.*;
import Alachisoft.NCache.Common.BitSet;
import Alachisoft.NCache.Common.Enum.TopicOperationType;
import Alachisoft.NCache.Common.Threading.Monitor;
import Alachisoft.NCache.Management.Statistics.StatisticsCounter;

import com.alachisoft.ncache.client.MessageItem;
import com.alachisoft.ncache.client.internal.caching.EventManager;
import com.alachisoft.ncache.client.internal.caching.*;
import Alachisoft.NCache.Common.ErrorHandling.ErrorCodes;
import Alachisoft.NCache.Common.ErrorHandling.ErrorMessages;
import com.alachisoft.ncache.runtime.caching.Topic;
import com.alachisoft.ncache.runtime.caching.WriteMode;
import com.alachisoft.ncache.runtime.caching.messaging.TopicPriority;
import com.alachisoft.ncache.runtime.caching.messaging.TopicSearchOptions;
import com.alachisoft.ncache.runtime.exceptions.CacheException;
import com.alachisoft.ncache.runtime.exceptions.OperationFailedException;
import com.alachisoft.ncache.runtime.util.TimeSpan;

import javax.xml.crypto.Data;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

import static com.alachisoft.ncache.runtime.caching.messaging.TopicSearchOptions.ByName;
import static com.alachisoft.ncache.runtime.caching.messaging.TopicSearchOptions.ByPattern;
import static tangible.DotNetToJavaStringHelper.isNullOrEmpty;

public class MessageManager implements IDisposable, TopicReRegister, PollNotificationListener {

    private static final int pollingInterval = 10; // polling interval is in seconds; i.e. 10 seconds
    private final ConcurrentHashMap topicsMap;
    private final EventManager eventManager;
    private final Object lockObj = new Object();
    private CacheImplBase cacheImpl;
    private Thread pollingThread;
    private boolean poll;
    private Date _lastPoll = new Date();
    private StatisticsCounter perfStatsCollector;
    private boolean isNotificationRegister = false;
    private long version;

    public MessageManager(EventManager eventManager, StatisticsCounter perfStatsCollector) {
        topicsMap = new ConcurrentHashMap();
        this.eventManager = eventManager;
        this.perfStatsCollector = perfStatsCollector;
    }

    public final void initialize() {
        eventManager.registerPollingEvent(this, EventTypeInternal.PubSub);
    }

    public final TopicReRegister getReregisterTopicListener() {
        return this;
    }

    public boolean getIsValidName(String topicName, TopicOperationType type) {
        if (type != TopicOperationType.GetPatternBased) {
            return true;
        }
        try {
            String regexPattern = MessageUtil.wildcardToRegex(topicName);
            Pattern.compile(regexPattern);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public final Topic getOrCreateTopic(TopicIdentity topicPair, TopicOperationType type, boolean internalOperation) throws CacheException {
        String topicName = topicPair.getTopicName();
        if (isNullOrEmpty(topicName)) {
            throw new IllegalArgumentException("Value cannot be null or empty." + System.lineSeparator() + "Parameter name: topicName");
        }

        if (!getIsValidName(topicName, type)) {
            throw new OperationFailedException(Alachisoft.NCache.Common.ErrorHandling.ErrorCodes.PubSub.INVALID_TOPIC_PATTERN, Alachisoft.NCache.Common.ErrorHandling.ErrorMessages.getErrorMessage(Alachisoft.NCache.Common.ErrorHandling.ErrorCodes.PubSub.INVALID_TOPIC_PATTERN));
        }

        if (!internalOperation && isDefaultTopicName(topicName)) {
            throw new OperationFailedException(Alachisoft.NCache.Common.ErrorHandling.ErrorCodes.PubSub.DEFAULT_TOPICS, Alachisoft.NCache.Common.ErrorHandling.ErrorMessages.getErrorMessage(Alachisoft.NCache.Common.ErrorHandling.ErrorCodes.PubSub.DEFAULT_TOPICS));
        }

        if (cacheImpl == null) {
            throw new OperationFailedException(Alachisoft.NCache.Common.ErrorHandling.ErrorCodes.CacheInit.CACHE_NOT_INIT, Alachisoft.NCache.Common.ErrorHandling.ErrorMessages.getErrorMessage(Alachisoft.NCache.Common.ErrorHandling.ErrorCodes.CacheInit.CACHE_NOT_INIT));
        }

        TopicImpl topic = null;
        TopicPriority topicPriority = topicPair.getTopicPriority();
        tangible.RefObject tempRef_topicPriority = new tangible.RefObject(topicPriority);
        if (cacheImpl.getOrCreate(topicName, tempRef_topicPriority, type)) {
            synchronized (this) {
                if(type == TopicOperationType.Get)
                    topicPair.setTopicPriority(tempRef_topicPriority.argvalue);

                topic = topicsMap.get(topicPair);
                if (topic != null) {
                    if (topic.getIsClosed()) {
                        topicsMap.remove(topicPair, topic);
                        TopicSearchOptions searchOptions = topic.getSearchOptions();
                        topic = new TopicImpl(topicName, topicPair.getTopicPriority(), cacheImpl, perfStatsCollector,
                                this);
                        topic.setSearchOptions(searchOptions);
                    }
                    topicsMap.putIfAbsent(topicPair, topic);
                    topic.IncrementRefCount();
                    return topic;
                }

                topic = new TopicImpl(topicName, topicPair.getTopicPriority(), cacheImpl, perfStatsCollector, this);
                if (type == TopicOperationType.GetPatternBased) {
                    topic.setSearchOptions(ByPattern);
                }

                topic.IncrementRefCount();
                topicsMap.putIfAbsent(topicPair, topic);
            }
        }
        return topic;
    }


    public final void stopPollingIfRequired(com.alachisoft.ncache.client.internal.caching.TopicImpl topic) {
        boolean keepPollingOn = false;

        if (topic == null || topic.getActiveSubscriptions() <= 0) {
            synchronized (this) {
                for (com.alachisoft.ncache.client.internal.caching.TopicImpl registerTopic : topicsMap.values()) {
                    if (registerTopic.getActiveSubscriptions() > 0 || registerTopic.getHasFailureDeliveryNotification()) {
                        keepPollingOn = true;
                        break;
                    }
                }

                if (!keepPollingOn) {
                    stopPolling();
                }
            }
        }
    }

    private void stopPolling() {

        try {
            synchronized (this) {
                poll = false;
                isNotificationRegister = false;

                if (pollingThread != null) {
                    pollingThread.interrupt();
                }
                pollingThread = null;
                if (ContinuousQueryManager.getTracingEnabled()) {
                    System.out.println("Thread : " + pollingThread.getName() + " stopped at " + new Date().toString());
                }
            }
        } catch (RuntimeException e) {
        }
    }

    public final void onSubscriptionCreated(TopicImpl topic, TopicSubscriptionImpl topicSubscription) {
        startPolling();
    }

    public final void startPolling() {
        synchronized (this) {
            if (!isNotificationRegister) {
                startPollForMessage();
                isNotificationRegister = true;
            }
        }
    }

    private void startPollForMessage() {
        poll = true;

        pollingThread = new Thread(new Runnable() {
            @Override
            public void run() {
                pollForMessage();
            }
        });
        pollingThread.setDaemon(true);
        pollingThread.setName(cacheImpl.getName() + ":TopicPollingThread");
        pollingThread.start();

        if (ContinuousQueryManager.getTracingEnabled()) {
            System.out.println("Thread : " + pollingThread.getName() + " started at " +  new Date().toString());
        }
    }

    private void pollForMessage() {
            long currentVersion = -1;
            while (poll) {
                try {
                    synchronized (lockObj) {
                        //wait only if there is no change event fired from server side causing verion update
                        if (currentVersion == version) {
                            Monitor.wait(lockObj,pollingInterval * 1000);
                        }
                    }
                    TimeSpan diff = TimeSpan.subtract(new Date(), _lastPoll);
//                    if (ContinuousQueryManager.getTracingEnabled()) {
//                        System.out.println("MessageMaanger.pollForMessage() -> Time Poll: " + diff.getMilliseconds());
//
//                    }
                    currentVersion = version;
                    poll();

                } catch (InterruptedException e) {
                    break;
                }catch (Exception e) {
                    //restarts poll if exception occured
                }
            }
    }

    private void poll() throws CacheException {
        BitSet flagMap = new BitSet();
        ReceivedMessages assignedResponse = cacheImpl.getMessageData(flagMap);
        if (assignedResponse != null) {
//            if (ContinuousQueryManager.getTracingEnabled()) {
//                for (Map.Entry> pair : map.entrySet()) {
//                        System.out.println("MessageMaanger.Poll() -> topic({ " + pair.getKey() + "})  message count :{ " + pair.getValue().size() + "}");
//                }
//            }
            sendMessageAcknowledgements(assignedResponse);
            deliverValidMessagesToClient(assignedResponse);
            topicValidation(assignedResponse);
        }
        _lastPoll=new Date();

    }

    @Override
    public final void dispose() {
        stopPolling();
        for (com.alachisoft.ncache.client.internal.caching.TopicImpl messageTopic : topicsMap.values()) {
            messageTopic.disposeInternal(false);
        }
        topicsMap.clear();
        cacheImpl = null;

        if (ContinuousQueryManager.getTracingEnabled()) {
            System.out.println("MessageManager disposed at " +  new Date().toString());
            Thread.dumpStack();
        }
    }

    @Override
    public final void onPollNotified() {
//        if (ContinuousQueryManager.getTracingEnabled()) {
//            System.out.println("MessageMaanger.onPollNotified() -> Time Poll: " + LocalDateTime.now().toString());
//        }
        synchronized (lockObj) {
            version++;
            Monitor.pulse(lockObj);
        }
    }

    public final void setCacheImpl(CacheImplBase value) {
        cacheImpl = value;
    }

    public final void topicDisposeItself(com.alachisoft.ncache.client.internal.caching.TopicImpl topic) {
        synchronized (this) {
            TopicIdentity topicPair = new TopicIdentity(topic.getName(), topic.getSearchOptions());
            TopicImpl existingTopic = topicsMap.get(topicPair);
            if (existingTopic != null && topic == existingTopic) {
                topicsMap.remove(topicPair);
            }
        }

    }

    public final void deleteTopic(String topicName) throws CacheException {
        if (cacheImpl == null) {
            throw new OperationFailedException(Alachisoft.NCache.Common.ErrorHandling.ErrorCodes.CacheInit.CACHE_NOT_INIT, ErrorMessages.resolveError(ErrorCodes.CacheInit.CACHE_NOT_INIT));
        }

        if (isDefaultTopicName(topicName)) {
            return;
        }
        TopicIdentity topicPair = new TopicIdentity(topicName, ByName);
        com.alachisoft.ncache.client.internal.caching.TopicImpl topic = topicsMap.get(topicPair);
        if (topicsMap.remove(topicPair, topic)) {
            if (topic.getSearchOptions() == ByName) {
                topic.disposeInternal(true);
                topic.fireDeleteNotification();
                stopPollingIfRequired(topic);
            }
        }
        cacheImpl.removeTopic(topicName, true);
    }

    private void sendMesasgeAcknowledgment(HashMap> acknowledgmentIdList) throws Exception {
        cacheImpl.acknowledgeMessageReceipt(acknowledgmentIdList);
    }

    @Override
    public void onTopicReregisterListener() throws Exception {
        for (TopicImpl topic : topicsMap.values()) {
            if (topic.getSearchOptions() == ByName) {
                TopicPriority topicPriority = topic.getPriority();
                tangible.RefObject tempRef_topicPriority = new tangible.RefObject(topicPriority);
                cacheImpl.getOrCreate(topic.getName(), tempRef_topicPriority, TopicOperationType.Create);
            }
            topic.reRegisterSubscribers(topic.getSearchOptions());
        }
    }

    public final boolean isDefaultTopicName(String topicName) {
        boolean isDefaultTopicName = false;

        if (!isNullOrEmpty(topicName)) {
            isDefaultTopicName = topicName.equals(TopicConstant.CQEventsTopic);
            isDefaultTopicName = isDefaultTopicName || topicName.equals(TopicConstant.GeneralEventsTopic);
            isDefaultTopicName = isDefaultTopicName || topicName.equals(TopicConstant.ItemLevelEventsTopic);
            isDefaultTopicName = isDefaultTopicName || topicName.equals(TopicConstant.CollectionEventsTopic);
        }
        return isDefaultTopicName;
    }

    private void sendMessageAcknowledgements(ReceivedMessages assignedResponse) throws CacheException {
        HashMap> ackIdList = new HashMap>();
        for (Map.Entry> pair : assignedResponse.getAssignedMessages().entrySet()) {
            List messageIds = new ArrayList();
            for (MessageItem messageItem : pair.getValue()) {
                messageIds.add(messageItem.getMessageId());
            }

            if (messageIds.size() > 0) {
                ackIdList.put(pair.getKey(), messageIds);
            }
        }

        if (ackIdList.size() > 0) {
            try {
                sendMesasgeAcknowledgment(ackIdList);
            } catch (Exception e) {
                throw new CacheException(e.getMessage(), e.getCause());
            }
        }
    }

    private void deliverValidMessagesToClient(ReceivedMessages assignedResponse) {
        int count = 0;

        for (Map.Entry> pair : assignedResponse.getAssignedMessages().entrySet()) {
            TopicImpl selectedTopic = null;
            for (TopicImpl topic : topicsMap.values()) {
                selectedTopic = getValidTopic(topic, pair.getKey());
                if (selectedTopic != null) {
//                    if (ContinuousQueryManager.getTracingEnabled()) {
////                        System.out.println("MessageMaanger.Poll() -> topic({ "+ selectedTopic +"})  message count :{ "+pair.getValue().size()+"}");
////                    }
                    if (pair.getValue() != null && pair.getValue().size() > 0) {
                        count += pair.getValue().size();
                        selectedTopic.updateSyncData(pair.getValue(), pair.getKey());
                    }
                }
            }
        }

        if (perfStatsCollector != null) {
            perfStatsCollector.incrementMessageDeliverPerSec(count);
        }

    }

    private void topicValidation(ReceivedMessages assignedResponse) throws CacheException {
        ArrayList removeList = new ArrayList();
        for (TopicImpl topic : topicsMap.values()) {
            TopicIdentity topicPair = new TopicIdentity(topic.getName(), topic.getSearchOptions());
            if (!assignedResponse.getAssignedMessages().containsKey(topic.getName()) && !assignedResponse.getRegisteredPatterns().contains(topic.getName())) {
                if (topic.getHasFailureDeliveryNotification() || topic.getActiveSubscriptions() > 0) {
                    if (topic.getSearchOptions() == ByName) {
                        if (getOrCreateTopic(topicPair, TopicOperationType.Get, true) != null) {
                            topic.reRegisterSubscribers(topic.getSearchOptions());
                        }
                    } else if (topic.getSearchOptions() == ByPattern) {
                        if (getOrCreateTopic(topicPair, TopicOperationType.GetPatternBased, true) != null) {
                            topic.reRegisterSubscribers(topic.getSearchOptions());
                        }
                    } else {
                        removeList.add(topic.getName());
                        topic.fireDeleteNotification();
                    }
                }
            }
        }
    }

    private TopicImpl getValidTopic(TopicImpl topic, String serverSideTopicName) {
        switch (topic.getSearchOptions()) {
            case ByPattern:
                String regexPattern = MessageUtil.wildcardToRegex(topic.getName());
                //Check Logic while Testing
                regexPattern.equalsIgnoreCase(serverSideTopicName);
                return topic;
            case ByName:
                if (topic.getName().equals(serverSideTopicName)) {
                    return topic;
                }
                break;
        }

        return null;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy