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

com.alachisoft.ncache.client.ContinuousQuery Maven / Gradle / Ivy

package com.alachisoft.ncache.client;

import Alachisoft.NCache.Common.BitSet;
import Alachisoft.NCache.Common.ResourcePool;
import Alachisoft.NCache.Common.Threading.ThreadPool;

import com.alachisoft.ncache.client.internal.caching.EventTypeInternal;
import com.alachisoft.ncache.runtime.events.EventDataFilter;
import com.alachisoft.ncache.runtime.events.EventType;
import com.alachisoft.ncache.runtime.exceptions.OperationFailedException;

import java.util.*;

/**
 * Class to hold Object query and values, intended for notifications.
 */
public class ContinuousQuery {
    //region Event Related Fields
    /**
     * @hidden
     */
    public short CQREFSTARTAdd = 2000;
    /**
     * @hidden
     */
    public short CQREFSTARTRemove = 3000;
    /**
     * @hidden
     */
    public short CQREFSTARTUpdate = 4000;
    String query;
    HashMap values;
    String serverUniqueId;
    String clientUniqueId;
    private QueryCommand _command;
    private QueryDataModificationListener queryNotificationCallback;
    private Object syncLock = new Object();
    private CacheEventDescriptor _descriptor;
    private int refadd = 0;
    private ResourcePool _cqAddEventPool = null;
    private ResourcePool _cqAddEventDataFilter = null;
    private short _cqAddCallbackRef = CQREFSTARTAdd;
    private EventDataFilter _cqAddDF = EventDataFilter.None;
    private int refremove = 0;
    private ResourcePool _cqRemoveEventPool = null;
    private ResourcePool _cqRemoveEventDataFilter = null;
    private short _cqRemoveCallbackRef = CQREFSTARTRemove;
    private EventDataFilter _cqRemoveDF = EventDataFilter.None;
    private int refupdate = 0;
    private ResourcePool _cqUpdateEventPool = null;
    private ResourcePool _cqUpdateEventDataFilter = null;
    private short _cqUpdateCallbackRef = CQREFSTARTUpdate;
    private EventDataFilter _cqUpdateDF = EventDataFilter.None;

    //endregion

    //region Getter Setters

    /**
     * Initializes a new instance of the ContinuousQuery class.
     * @param command {@link QueryCommand} containing query text and values.
     */
    public ContinuousQuery(QueryCommand command) {
        this._command = command;
        this.query = command.getQuery();
        this.values = command.getParameters();
        this.clientUniqueId = UUID.randomUUID().toString();
    }

    /**
     * Gets the query command for the continuous query.
     * @return The QueryCommand instance.
     */
    public QueryCommand getQueryCommand() {
        return _command;
    }

    /**
     * Sets the query command for the continuous query.
     * @param value The QueryCommand instance.
     */
    public void setQueryCommand(QueryCommand value) {
        _command = value;
    }

    /**
     * @hidden
     * @return
     */
    public String getQueryInternal() {
        return this.query;
    }

    private void setQuery(String query) {
        this.query = query;
    }

    /**
     * @hidden
     * @return
     */
    public HashMap getValuesInternal() {
        return this.values;
    }

    private void setValues(HashMap values) {
        this.values = values;
    }

    /**
     * @hidden
     * @return
     */
    public String getServerUniqueIDInternal() {
        return this.serverUniqueId;
    }

    /**
     * @hidden
     * @param serverUniqueId
     * @throws OperationFailedException
     */
    public void setServerUniqueIDInternal(String serverUniqueId) throws OperationFailedException {

        if (serverUniqueId == null)
            throw new OperationFailedException("serverUniqueId is null");

        this.serverUniqueId = serverUniqueId;
    }

    /**
     * @hidden
     * @return
     */
    public String getClientUniqueIDInternal() {
        return this.clientUniqueId;
    }


    //endregion

    /**
     * @hidden
     * @param eventype
     * @return
     */
    public EventDataFilter maxFilterInternal(EventType eventype) {
        switch (eventype) {
            case ItemAdded:
                return _cqAddDF;
            case ItemRemoved:
                return _cqRemoveDF;
            case ItemUpdated:
                return _cqUpdateDF;
            default:
                return EventDataFilter.None;
        }
    }

    private void registerCQ(QueryDataModificationListener listener, EnumSet eventEnumSet, EventDataFilter datafilter) throws java.lang.IllegalArgumentException {
        if (listener == null) {
            throw new java.lang.IllegalArgumentException("Value cannot be null."+System.lineSeparator()+"Parameter name: listener");
        }

        for (EventType type : EventType.values()) {
            synchronized (syncLock) {
                ResourcePool pool = null;
                ResourcePool poolDF = null;

                if (type.equals(EventType.ItemAdded) && eventEnumSet.contains(EventType.ItemAdded)) {
                    pool = _cqAddEventPool;
                    poolDF = _cqAddEventDataFilter;
                } else if (type.equals(EventType.ItemRemoved) && eventEnumSet.contains(EventType.ItemRemoved)) {
                    pool = _cqRemoveEventPool;
                    poolDF = _cqRemoveEventDataFilter;
                } else if (type.equals(EventType.ItemUpdated) && eventEnumSet.contains(EventType.ItemUpdated)) {
                    pool = _cqUpdateEventPool;
                    poolDF = _cqUpdateEventDataFilter;
                }

                if (pool == null) {
                    continue;
                }

                while (true) {

                    if (pool.GetResource(listener) == null) {
                        short refcallback = -1;
                        if (type == EventType.ItemAdded) {
                            refcallback = ++_cqAddCallbackRef;
                            _cqAddDF = _cqAddDF.getValue() < datafilter.getValue() ? datafilter : _cqAddDF;
                        } else if (type == EventType.ItemRemoved) {
                            refcallback = ++_cqRemoveCallbackRef;
                            _cqRemoveDF = _cqRemoveDF.getValue() < datafilter.getValue() ? datafilter : _cqRemoveDF;
                        } else {
                            refcallback = ++_cqUpdateCallbackRef;
                            _cqUpdateDF = _cqUpdateDF.getValue() < datafilter.getValue() ? datafilter : _cqUpdateDF;
                        }

                        pool.AddResource(listener, refcallback);
                        poolDF.AddResource(refcallback, datafilter);
                        break;
                    } else {
                        try {
                            Object value = pool.GetResource(listener);
                            short cref = value != null ? Short.parseShort(value.toString()) : -100;
                            if (cref < 0) {
                                break; //FAIL CONDITION
                            }
                            poolDF.RemoveResource(cref);
                            pool.RemoveResource(listener);

                            //add it again into the table for updating ref count.
                            pool.AddResource(listener, cref);
                            poolDF.AddResource(cref, datafilter);
                            break;
                        } catch (NullPointerException e) {
                            //Legacy code: can create an infinite loop
                            //Recomendation of returning a negative number instead of continue
                            continue;
                        }
                    }
                }
            }
        }
    }

    /**
     * @hidden
     * @param key
     * @param eventType
     * @param item
     * @param oldItem
     * @param notifyAsync
     * @param cacheName
     * @param flag
     * @param datafilter
     */
    public void fireCQEventsInternal(final String key, EventTypeInternal eventType, EventCacheItem item, EventCacheItem oldItem, boolean notifyAsync, String cacheName, BitSet flag, EventDataFilter datafilter) {
        try {
            CQEventArg arg = null;
            int collectionCount = 0;
            Iterator collection = null;
            ResourcePool pool = null;
            ResourcePool filterPool = null;
            Set keySet = null;

            if (eventType == EventTypeInternal.ItemAdded && _cqAddEventPool != null) {
                pool = _cqAddEventPool;
                keySet = (Set) _cqAddEventPool.getKeys();
                if (keySet != null && keySet.size() > 0) {
                    collection = keySet.iterator();
                    collectionCount = keySet.size();
                    filterPool = _cqAddEventDataFilter;
                }
            } else if (eventType == EventTypeInternal.ItemUpdated && _cqUpdateEventPool != null) {
                pool = _cqUpdateEventPool;
                keySet = (Set) _cqUpdateEventPool.getKeys();
                if (keySet != null && keySet.size() > 0) {
                    collection = keySet.iterator();
                    collectionCount = keySet.size();
//                        tempArr = (QueryDataModificationListener[]) keySet.toArray();
                    filterPool = _cqUpdateEventDataFilter;
                }
            } else if (eventType == EventTypeInternal.ItemRemoved && _cqRemoveEventPool != null) {
                pool = _cqRemoveEventPool;
                keySet = (Set) _cqRemoveEventPool.getKeys();
                if (keySet != null && keySet.size() > 0) {
                    collection = keySet.iterator();
                    collectionCount = _cqRemoveEventPool.getKeys().size();
//                        tempArr = (QueryDataModificationListener[]) keySet.toArray();
                    filterPool = _cqRemoveEventDataFilter;
                }
            } else
                return;

            if (collection != null && collectionCount > 0)  // > 0
            {
                for (QueryDataModificationListener listener : keySet) {
                    short index = -1;
                    Object obj = pool.GetResource(listener);
                    if (obj != null)
                        index = Short.parseShort(obj.toString());

                    if (index > -1) {
                        //Not to fire event if datafilter recieved is less than requried OR noDF present
                        //if (filterPool.GetResource(index) == null && (datafilter < (EventDataFilter)filterPool.GetResource(index)) )
                        //    continue;
                        EventDataFilter queryDataFilter = (EventDataFilter) filterPool.GetResource(index);

                        if (eventType == EventTypeInternal.ItemAdded)
                            arg = createCQEventArgument(queryDataFilter, key, cacheName, EventType.ItemAdded, item, oldItem);
                        else if (eventType == EventTypeInternal.ItemRemoved)
                            arg = createCQEventArgument(queryDataFilter, key, cacheName, EventType.ItemRemoved, item, oldItem);
                        else if (eventType == EventTypeInternal.ItemUpdated)
                            arg = createCQEventArgument(queryDataFilter, key, cacheName, EventType.ItemUpdated, item, oldItem);
                        else
                            return;

                        arg.setContinuousQuery(this);
                        final CQEventArg finalArg = arg;

                        if (notifyAsync) {
                            Runnable runable= new Runnable() {
                                @Override
                                public void run() {
                                    listener.onQueryDataModified(key,finalArg);
                                }
                            } ;
                            ThreadPool.getInstance().executeTask(runable);
                        } else {
                            listener.onQueryDataModified(key, arg);
                        }
                    }
                }
            }
        } catch (Exception ex) {
        }
    }

    private CQEventArg createCQEventArgument(EventDataFilter dataFilter, String key, String cacheName, EventType eventType, EventCacheItem item, EventCacheItem oldItem) {

        EventCacheItem cloneItem = null;
        EventCacheItem cloneOldItem = null;

        if (dataFilter != EventDataFilter.None && item != null) {
            Object temp = item.clone();
            cloneItem = temp instanceof EventCacheItem ? (EventCacheItem) temp : null;

            if (cloneItem != null && dataFilter == EventDataFilter.Metadata) {
                cloneItem.setValue(null);
            }
        }

        if (oldItem != null && dataFilter != EventDataFilter.None) {
            Object temp = oldItem.clone();
            cloneOldItem = temp instanceof EventCacheItem ? (EventCacheItem) temp : null;

            if (dataFilter == EventDataFilter.Metadata && cloneOldItem != null) {
                cloneOldItem.setValue(null);
            }
        }


        CQEventArg eventArg = new CQEventArg(cacheName, eventType, cloneItem, null);
        if (eventType == EventType.ItemUpdated) {
            eventArg.setOldItem(cloneOldItem);
        }
        return eventArg;
    }

    /**
     * This method registers a custom listener that is fired on change in dataset of a continuous query
     * @param listener The listener that is invoked whenever there is a change in dataset of continuous Query.
     * @param eventEnumSet Registers the listener with the specified event types in the enumset.
     * @param datafilter This enum is to describe when registering an event, upon raise how much data is retrieved from cache when the event is raised.
     * @throws java.lang.IllegalArgumentException
     */
    public void addDataModificationListener(QueryDataModificationListener listener, EnumSet eventEnumSet, EventDataFilter datafilter) throws java.lang.IllegalArgumentException {

        if (listener != null) {
            //Avoiding new ResourcePool(inside = new Hashtable) at constructor level
            if (_cqAddEventPool == null && eventEnumSet.contains(EventType.ItemAdded)) {
                _cqAddEventPool = new ResourcePool();
                _cqAddEventDataFilter = new ResourcePool();
            }
            if (_cqRemoveEventPool == null && eventEnumSet.contains(EventType.ItemRemoved)) {
                _cqRemoveEventPool = new ResourcePool();
                _cqRemoveEventDataFilter = new ResourcePool();
            }
            if (_cqUpdateEventPool == null && eventEnumSet.contains(EventType.ItemUpdated)) {
                _cqUpdateEventPool = new ResourcePool();
                _cqUpdateEventDataFilter = new ResourcePool();
            }


            //CacheEventDiscriptor discriptor = CacheEventDiscriptor.CreateCacheDiscriptor(eventType, _cacheName, listener, datafilter);
            registerCQ(listener, eventEnumSet, datafilter);
        }
    }

    /**
     * This method unregisters a custom listener that is fired on change in dataset of a continuous query.
     * @param listener The listener that was registered with continuous query.
     * @param eventEnumSet Unregisters the listener with the specified event types in the enumset.
     * @throws IllegalArgumentException
     */
    public void removeDataModificationListener(QueryDataModificationListener listener, EnumSet eventEnumSet) throws IllegalArgumentException {
        //BY LEGACY DESIGN THERE IS NO UNREGISTRATION PROCESS

        if (listener == null)
            throw new IllegalArgumentException("callback");

        Object id = -1;

        for (EventType type : EventType.values()) {
            synchronized (syncLock) {
                ResourcePool pool = null;
                ResourcePool poolDF = null;

                if (type.equals(EventType.ItemAdded) && eventEnumSet.contains(EventType.ItemAdded)) {
                    pool = _cqAddEventPool;
                    poolDF = _cqAddEventDataFilter;
                } else if (type.equals(EventType.ItemRemoved) && eventEnumSet.contains(EventType.ItemRemoved)) {
                    pool = _cqRemoveEventPool;
                    poolDF = _cqRemoveEventDataFilter;
                } else if (type.equals(EventType.ItemUpdated) && eventEnumSet.contains(EventType.ItemUpdated)) {
                    pool = _cqUpdateEventPool;
                    poolDF = _cqUpdateEventDataFilter;
                }

                if (pool == null)
                    continue;

                Object temp = pool.GetResource(listener);
                short index = -1;

                if (temp != null)
                    index = Short.parseShort(temp.toString());

                if (index > -1) {
                    Object retVal = poolDF.RemoveResource(index);
                    pool.RemoveResource(listener);

                    if (retVal == null) continue;
                    boolean unregisterNotification = poolDF.getCount() == 0;
                    EventDataFilter maxDataFilter = EventDataFilter.None;

                    if (!unregisterNotification) {
                        Object[] callbackRefs = poolDF.GetAllResourceKeys();
                        if (callbackRefs != null) {
                            for (int i = 0; i < callbackRefs.length; i++) {
                                EventDataFilter df = (EventDataFilter) callbackRefs[i];

                                if (df.getValue() > maxDataFilter.getValue())
                                    maxDataFilter = df;

                                if (maxDataFilter.equals(EventDataFilter.DataWithMetadata)) break;
                            }
                        }
                    }

                    if (type == EventType.ItemAdded)
                        _cqAddDF = maxDataFilter;
                    else if (type == EventType.ItemRemoved)
                        _cqRemoveDF = maxDataFilter;
                    else
                        _cqUpdateDF = maxDataFilter;


                }
            }
        }

    }

    /**
     * @hidden
     * @return
     */
    public boolean notifyAddInternal() {
        if (_cqAddEventPool != null && _cqAddEventPool.getCount() > 0) {
            return true;
        }
        return false;
    }

    /**
     * @hidden
     * @return
     */
    public boolean notifyUpdateInternal() {
        if (_cqUpdateEventPool != null && _cqUpdateEventPool.getCount() > 0) {
            return true;
        }
        return false;
    }

    /**
     * @hidden
     * @return
     */
    public boolean notifyRemoveInternal() {
        if (_cqRemoveEventPool != null && _cqRemoveEventPool.getCount() > 0) {
            return true;
        }
        return false;
    }

    /**
     * Indicates whether some other object is "equal to" this one.
     * 

* The {@code equals} method implements an equivalence relation * on non-null object references: *

    *
  • It is reflexive: for any non-null reference value * {@code x}, {@code x.equals(x)} should return * {@code true}. *
  • It is symmetric: for any non-null reference values * {@code x} and {@code y}, {@code x.equals(y)} * should return {@code true} if and only if * {@code y.equals(x)} returns {@code true}. *
  • It is transitive: for any non-null reference values * {@code x}, {@code y}, and {@code z}, if * {@code x.equals(y)} returns {@code true} and * {@code y.equals(z)} returns {@code true}, then * {@code x.equals(z)} should return {@code true}. *
  • It is consistent: for any non-null reference values * {@code x} and {@code y}, multiple invocations of * {@code x.equals(y)} consistently return {@code true} * or consistently return {@code false}, provided no * information used in {@code equals} comparisons on the * objects is modified. *
  • For any non-null reference value {@code x}, * {@code x.equals(null)} should return {@code false}. *
*

* The {@code equals} method for class {@code Object} implements * the most discriminating possible equivalence relation on objects; * that is, for any non-null reference values {@code x} and * {@code y}, this method returns {@code true} if and only * if {@code x} and {@code y} refer to the same object * ({@code x == y} has the value {@code true}). *

* Note that it is generally necessary to override the {@code hashCode} * method whenever this method is overridden, so as to maintain the * general contract for the {@code hashCode} method, which states * that equal objects must have equal hash codes. * * @param obj the reference object with which to compare. * @return {@code true} if this object is the same as the obj * argument; {@code false} otherwise. * @see #hashCode() * @see java.util.HashMap */ @Override public boolean equals(Object obj) { if (obj != null && obj instanceof ContinuousQuery) { ContinuousQuery other = (ContinuousQuery) obj; if (this.clientUniqueId.equals(other.clientUniqueId)) { return true; } } return false; } /** * Returns a hash code value for the object. This method is * supported for the benefit of hash tables such as those provided by * {@link java.util.HashMap}. *

* The general contract of {@code hashCode} is: *

    *
  • Whenever it is invoked on the same object more than once during * an execution of a Java application, the {@code hashCode} method * must consistently return the same integer, provided no information * used in {@code equals} comparisons on the object is modified. * This integer need not remain consistent from one execution of an * application to another execution of the same application. *
  • If two objects are equal according to the {@code equals(Object)} * method, then calling the {@code hashCode} method on each of * the two objects must produce the same integer result. *
  • It is not required that if two objects are unequal * according to the {@link java.lang.Object#equals(java.lang.Object)} * method, then calling the {@code hashCode} method on each of the * two objects must produce distinct integer results. However, the * programmer should be aware that producing distinct integer results * for unequal objects may improve the performance of hash tables. *
*

* As much as is reasonably practical, the hashCode method defined * by class {@code Object} does return distinct integers for * distinct objects. (The hashCode may or may not be implemented * as some function of an object's memory address at some point * in time.) * * @return a hash code value for this object. * @see java.lang.Object#equals(java.lang.Object) * @see java.lang.System#identityHashCode */ @Override public int hashCode() { if (this.clientUniqueId != null) { return this.clientUniqueId.hashCode(); } else { return super.hashCode(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy