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

com.alachisoft.ncache.client.internal.caching.CacheImpl Maven / Gradle / Ivy

There is a newer version: 5.3.0
Show newest version
package com.alachisoft.ncache.client.internal.caching;

import Alachisoft.NCache.Caching.AutoExpiration.ExpirationHint;
import Alachisoft.NCache.Caching.CacheEntry;
import Alachisoft.NCache.Caching.CallbackEntry;
import Alachisoft.NCache.Caching.CompressedValueEntry;
import Alachisoft.NCache.Caching.Queries.QueryResultSet;
import Alachisoft.NCache.Common.BitSet;
import Alachisoft.NCache.Common.*;
import Alachisoft.NCache.Common.DataSource.ClientCacheItem;
import Alachisoft.NCache.Common.DataStructures.EnumerationDataChunk;
import Alachisoft.NCache.Common.DataStructures.EnumerationPointer;
import Alachisoft.NCache.Common.Enum.SerializationFormat;
import Alachisoft.NCache.Common.Enum.UserObjectType;
import Alachisoft.NCache.Common.Locking.LockAccessType;
import Alachisoft.NCache.Common.Queries.AverageResult;
import Alachisoft.NCache.Common.Stats.UsageStats;
import Alachisoft.NCache.Common.Threading.ThreadPool;
import Alachisoft.NCache.Management.CacheConfig;
import Alachisoft.NCache.Management.Statistics.StatisticsCounter;

import com.alachisoft.ncache.client.*;
import com.alachisoft.ncache.client.CacheReader;
import com.alachisoft.ncache.client.internal.util.ClientConfiguration;
import com.alachisoft.ncache.client.internal.util.ConversionUtil;
import com.alachisoft.ncache.client.internal.util.DictionaryEntry;
import com.alachisoft.ncache.client.internal.util.HelperUtil;
import com.alachisoft.ncache.client.services.MessagingService;
import com.alachisoft.ncache.client.services.NotificationService;
import com.alachisoft.ncache.client.services.SearchService;
import com.alachisoft.ncache.common.caching.EntryType;
import Alachisoft.NCache.Common.ErrorHandling.ErrorCodes;
import Alachisoft.NCache.Common.ErrorHandling.ErrorMessages;
import com.alachisoft.ncache.runtime.CacheItemPriority;
import com.alachisoft.ncache.runtime.JSON.JsonObject;
import com.alachisoft.ncache.runtime.JSON.JsonValue;
import com.alachisoft.ncache.runtime.JSON.JsonValueBase;
import com.alachisoft.ncache.runtime.caching.*;
import com.alachisoft.ncache.runtime.caching.expiration.Expiration;
import com.alachisoft.ncache.runtime.caching.expiration.ExpirationConstants;
import com.alachisoft.ncache.runtime.caching.expiration.ExpirationType;
import com.alachisoft.ncache.runtime.dependencies.CacheDependency;
import com.alachisoft.ncache.runtime.events.EventDataFilter;
import com.alachisoft.ncache.runtime.events.EventType;
import com.alachisoft.ncache.runtime.events.ListenerType;
import com.alachisoft.ncache.runtime.exceptions.SecurityException;
import com.alachisoft.ncache.runtime.exceptions.*;
import com.alachisoft.ncache.runtime.exceptions.runtime.GeneralFailureRuntimeException;
import com.alachisoft.ncache.runtime.exceptions.runtime.OperationFailedRuntimeException;
import com.alachisoft.ncache.runtime.util.NCDateTime;
import com.alachisoft.ncache.runtime.util.TimeSpan;
import com.alachisoft.ncache.serialization.core.io.surrogates.NCacheArgumentException;
import com.alachisoft.ncache.serialization.standard.FormatterServices;
import com.alachisoft.ncache.serialization.util.SerializationUtil;
import com.alachisoft.ncache.serialization.util.TypeInfoMap;

import tangible.RefObject;

import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicInteger;
import static com.alachisoft.ncache.serialization.standard.CompactBinaryFormatter.toByteBuffer;
import static tangible.DotNetToJavaStringHelper.isNullOrEmpty;

public  class CacheImpl implements Cache {
    //region Fields
    public static TimeSpan NoLockingExpiration = TimeSpan.Zero;
    public static java.util.Date NoAbsoluteExpiration= ExpirationConstants.AbsoluteNoneExpiration;
    public static Date defaultAbsolute =ExpirationConstants.AbsoluteDefaultExpiration;
    public static final TimeSpan NoSlidingExpiration=ExpirationConstants.SlidingNoneExpiration;
    public static Date defaultAbsoluteLonger=ExpirationConstants.AbsoluteDefaultLongerExpiration;
    public static final TimeSpan DefaultSliding = ExpirationConstants.SlidingDefaultExpiration;
    public static final TimeSpan DefaultSlidingLonger = ExpirationConstants.SlidingDefaultLongerExpiration;

    private static boolean _serializationEnabled = true;
    public String _cacheId;
    public MessagingServiceImpl _messagingService;
    boolean _clientCacheEnabled = false;
    FormatterServices impl;
    NotificationService _notificationService;
    StatisticsCounter _perfStatsCollector;
    boolean _exceptionEnabled = true;
    //region notification service
    ArrayList cacheStoppedListeners = new ArrayList();
    ArrayList cacheClearedListeners = new ArrayList();
    ArrayList memberJoinedListeners = new ArrayList();
    ArrayList memberLeftListeners = new ArrayList();
    ArrayList cacheClientConnectivityChangedListeners = new ArrayList();
    private GeneralDataNotificationWrapper _notificationWrapper = null;
    private CacheClearedListener _cqCacheClearListener;
    private CacheEventsListener _listener = null;
    private AtomicInteger _refCount = new AtomicInteger(0);
    private int _refCacheStoppedCount = 0;
    private int _refClearCount = 0;
    private int _refCustomCount = 0;
    private TimeSpan noLockExpiration = TimeSpan.Zero;
    private ResourcePool _callbackIDsMap = new ResourcePool();
    private ResourcePool _callbacksMap = new ResourcePool();

    // endregion

    private int _refCounter = 0;
    private RemoteCacheClusterEventsListener _clusterListener;
    private ArrayList _secondaryInprocInstances;
    private SearchService _searchService;
    private SerializationFormat _serializationFormat;
    private CacheConfig _config;
    private CacheAsyncEventsListener _asyncListener;
    // region Cache
    private CacheImplBase _cacheImpl;
    private String _serializationContext;
    private EventManager _eventManager;
    private int _compressed = BitSetConstants.Compressed;
    ResourcePool _asyncCallbackIDsMap = new ResourcePool();
    private ResourcePool _asyncCallbacksMap = new ResourcePool();
    private short _dsiacbInitialVal = 6000;
    private short _dsiucbInitialVal = 7000;
    private short _dsircbInitialVal = 8000;
    private long _compressionThresholdSize = 0;
    private boolean _compressionEnabled = false;
    boolean _encryptionEnabled = false;
    private com.alachisoft.ncache.serialization.util.TypeInfoMap typeMap = null;
    private long size;
    private WebCacheEnumerator enumerator;

    ///endregion

    ///endregion

    ///region Constructors
    public CacheImpl() {
        _notificationWrapper = new GeneralDataNotificationWrapper(this);
        _eventManager = new EventManager(_cacheId, null, this);
        _listener = new CacheEventsListener(this, _eventManager);

    }
    public CacheImpl(CacheImplBase objectCache, CacheConfig config) throws CacheException {
        _notificationWrapper = new GeneralDataNotificationWrapper(this);
        _cacheImpl = objectCache;
        _config = config;
        _cacheId = config.getCacheId();

        if (_cacheImpl != null) {
            _serializationContext = _cacheImpl.getName();
            _cacheId = _cacheImpl.getName();
        }

        _eventManager = new EventManager(_cacheId, null, this);
        _listener = new CacheEventsListener(this, _eventManager);
        _asyncListener = new CacheAsyncEventsListener(this);
        _clusterListener = new RemoteCacheClusterEventsListener(this);

        loadRWTrhuSettings();
        addRef();
        _messagingService = new MessagingServiceImpl(_eventManager, null, this);

        _searchService = new SearchServiceImpl(this);
        _notificationService = new NotificationServiceImpl(this);

    }
    public CacheImpl(CacheImplBase objectCache, String cacheId, StatisticsCounter perfStatsCollector) throws CacheException {
        _notificationWrapper = new GeneralDataNotificationWrapper(this);

        _cacheImpl = objectCache;
        _cacheId = cacheId;
        if (_cacheImpl != null) {
            _serializationContext = _cacheImpl.getName(); //Sets the serialization context.
        }
        _eventManager = new EventManager(_cacheId, null, this);
        _listener = new CacheEventsListener(this, _eventManager);
        _asyncListener = new CacheAsyncEventsListener(this);
        _clusterListener = new RemoteCacheClusterEventsListener(this);

        _perfStatsCollector = perfStatsCollector;

        if (_perfStatsCollector != null) {
            _perfStatsCollector.setPollRequestsCount(0);
            _perfStatsCollector.setPollLastUpdates(0);
            _perfStatsCollector.setPollLastRemoves(0);
        }



        loadRWTrhuSettings();
        addRef();

        _messagingService = new MessagingServiceImpl(_eventManager, perfStatsCollector, this);
    }
    ///endregion

    ///region getters
    public  CacheImpl getCacheInstanceInternal()
    {
        return this;
    }
    public ArrayList getMemberJoinedListeners() {
        return memberJoinedListeners;
    }


    public ArrayList getMemberLeftListeners() {
        return memberLeftListeners;
    }

    public ArrayList getCacheClientConnectivityChangedListeners() {
        return cacheClientConnectivityChangedListeners;
    }

    public boolean getExceptionEnabled() {
        return _exceptionEnabled;
    }

    public void setExceptionEnabled(boolean exceptionEnabled) {
        _exceptionEnabled = exceptionEnabled;
    }

    @Override
    public com.alachisoft.ncache.client.ClientInfo getClientInfo() throws CacheException {
        return _cacheImpl.getLocalClientInfo();
    }
    public List getConnectedClientList() throws CacheException {
        return _cacheImpl.getConnectedClientList();
    }

    ///region private
    private static String[] toArray(Iterable values) {
        ArrayList list = new ArrayList();
        for (String item : values) {
            list.add(item);
        }
        String[] array = list.toArray(new String[list.size()]);
        return array;
    }

    SerializationFormat getSerializationFormat() {
        return _serializationFormat;
    }

    void setSerializationFormat(SerializationFormat _serializationFormat) {
        this._serializationFormat = _serializationFormat;
    }



    public void setMessagingServiceCacheImpl(CacheImplBase cacheImpl) {
        _messagingService.getPubSubManager().setCacheImpl(cacheImpl);
    }

    public RemoteCacheClusterEventsListener getClusterListener() {
        return _clusterListener;
    }

    public CacheAsyncEventsListener getAsyncListener() {
        return _asyncListener;
    }

    CacheEventsListener getEventListener() {
        return _listener;
    }


    public String getSerializationContext() {
        return _serializationContext;
    }

    void setSerializationContext(String value) {
        _serializationContext = value;
    }

    public EventManager getEventManager() {
        return _eventManager;
    }

    public ArrayList getCacheStoppedListeners() {
        return cacheStoppedListeners;
    }

    public ArrayList getCacheClearedListeners() {
        return cacheClearedListeners;
    }

    ///endregion

    ///endregion

    ///region Others
    @Override
    public void close(){
        dispose(true);
    }
//    @Override
//    public boolean updateAttributes(String key, CacheItemAttributes attributes) throws CacheException {
//        validateKey(key);
//
//        if (attributes == null)
//        {
//            throw new IllegalArgumentException("Value cannot be null."+System.lineSeparator()+"Parameter name: attribute");
//        }
//
//        return _cacheImpl.setAttributes(key, attributes);
//    }
    ///endregion

    ///region CRUD

    ///region AddOperations
    @Override
    public CacheItemVersion add(String key, Object value) throws CacheException, java.lang.IllegalArgumentException {
        Expiration expiration = new Expiration(ExpirationType.DefaultAbsolute);
        CacheItem cacheItem = new CacheItem(value);
        cacheItem.setExpiration(expiration);
        return add(key, cacheItem);
    }

    @Override

   public CacheItemVersion add(String key, CacheItem item) throws CacheException, java.lang.IllegalArgumentException {
        return AddInternal(key, item);
    }

    private CacheItemVersion AddInternal(String key, CacheItem item) throws CacheException, IllegalArgumentException {
        RefObject size = new RefObject<>(Long.valueOf(0));
        if (item == null) {
            throw new IllegalArgumentException("Value cannot be null."+System.lineSeparator()+"Parameter name: CacheItem");
        }

        String providerName = null;

        EnumSet eventTypes = EnumSet.of(EventTypeInternal.None);


        RefObject tempRef_providerName = new RefObject(providerName);
        RefObject> tempRef_eventTypes = new RefObject(eventTypes);

        providerName = tempRef_providerName.argvalue;

        eventTypes = tempRef_eventTypes.argvalue;
        //Consider only first element from enumset. For now multiple events are not supported.
        EventTypeInternal eventTypeInternal = (EventTypeInternal) eventTypes.toArray()[0];

        boolean ResyncOnExpirationEnable= false;


        return addOperation(key, item.getValue(Object.class),
        item.getDependency(), item.getSyncDependency(), ConversionUtil.getAbsoluteExpiration(item.getExpiration()), ConversionUtil.getSlidingExpiration(item.getExpiration()), item.getCacheItemPriority(),
        eventTypeInternal ,ResyncOnExpirationEnable, item.getGroup(),
        null,
        CacheItemWrapperInternal.getCacheItemUpdatedListener(item), CacheItemWrapperInternal.getCacheItemRemovedListener(item),
        CacheItemWrapperInternal.getItemUpdatedDataFilter(item), CacheItemWrapperInternal.getItemRemovedDataFilter(item),
        size, true, null, (short) -1, (short) -1, (short) -1);

    }

//    //region AddAsync
//    @Override
//    public FutureTask addAsync(String key, Object value)  {
//        Expiration expiration = new Expiration(ExpirationType.DefaultAbsolute);
//        CacheItem cacheItem = new CacheItem(value);
//        cacheItem.setExpiration(expiration);
//        return addAsync(key, cacheItem);
//    }
//
//    @Override
//    public FutureTask addAsync(String key, CacheItem item) {
//        return addAsync(key, item, new WriteThruOptions(WriteMode.None, ""));
//    }
//
//    @Override
//    public FutureTask addAsync(String key, CacheItem item, WriteThruOptions writeThruOptions) {
//        AddAsyncCallable task = new AddAsyncCallable(key, item, writeThruOptions);
//        FutureTask futureTask = new FutureTask(task);
//        ThreadPool.getInstance().executeTask(futureTask);
//        return futureTask;
//    }
    protected CacheItemVersion addOperation(String key, Object value, CacheDependency dependency, CacheSyncDependency syncDependency, Date absoluteExpiration, TimeSpan slidingExpiration, CacheItemPriority priority,
                                  EventTypeInternal eventTypeInternal ,boolean isResyncExpiredItems, String group,  String providerName,
                                  CacheDataModificationListener cacheItemUpdatedListener,CacheDataModificationListener cacheItemRemovedListener,EventDataFilter itemUpdateDataFilter, EventDataFilter itemRemovedDataFilter, RefObject size, boolean allowQueryTags, String clientId,
                                  short updateCallbackId, short removeCallbackId, short dsItemAddedCallbackId)
            throws CacheException, java.lang.IllegalArgumentException {
        if (_cacheImpl == null)
        {
            throw new OperationFailedException(ErrorCodes.CacheInit.CACHE_NOT_INIT, ErrorMessages.getErrorMessage(ErrorCodes.CacheInit.CACHE_NOT_INIT));
        }
        validateKeyValue(key, value);

        String typeName = null;
        java.util.HashMap queryInfo = null;
        Alachisoft.NCache.Common.BitSet flagMap = new BitSet();


        //client cache item
        if (value instanceof ClientCacheItem)
        {
            ClientCacheItem clientcacheItem = (ClientCacheItem)((value instanceof ClientCacheItem) ? value : null);
            value=clientcacheItem.getValue();
            queryInfo=clientcacheItem.getQueryInfo();

            if (clientcacheItem.getFlags().IsAnyBitSet((byte) BitSetConstants.BinaryData))
            {
                flagMap.SetBit((byte) BitSetConstants.BinaryData);
            }

            else if (clientcacheItem.getFlags().IsAnyBitSet((byte) BitSetConstants.JsonData))
            {
                flagMap.SetBit((byte) BitSetConstants.JsonData);
            }
            typeName=clientcacheItem.getGroupDataType();
        }
        if (!tangible.DotNetToJavaStringHelper.isNullOrEmpty(group) && tangible.DotNetToJavaStringHelper.isNullOrEmpty(typeName))
        {
            typeName = getTypeName(value);
        }

        if (!tangible.DotNetToJavaStringHelper.isNullOrEmpty(providerName))
        {
            providerName.toLowerCase();
        }

        if(itemUpdateDataFilter == null){
            itemUpdateDataFilter = EventDataFilter.None;
        }
        if(itemRemovedDataFilter == null){
            itemRemovedDataFilter = EventDataFilter.None;
        }
        UsageStats stats = new UsageStats();
        stats.BeginSample();

        //populate queryinfo
        if (queryInfo == null ) {
            queryInfo = new HashMap();
            if (allowQueryTags) {
                HashMap htQueryInfo= getQueryInfo(value);
                if(htQueryInfo !=null)
                    queryInfo.put("query-info",htQueryInfo);


            }
        }
        value= safeSerialize(value,_serializationContext,flagMap,size.getValue(),UserObjectType.CacheItem);

        if (absoluteExpiration != null && absoluteExpiration!= NoAbsoluteExpiration)
        {
            try {
                absoluteExpiration= NCDateTime.getUTCDate(absoluteExpiration);
            } catch (ParseException ignored) {
            }

        }


        if (removeCallbackId == -1) {
            if (cacheItemRemovedListener != null) {

                short[] callbackIds = _eventManager.registerSelectiveEvent(cacheItemRemovedListener, EnumSet.of(EventType.ItemRemoved), itemRemovedDataFilter);
                removeCallbackId = callbackIds[1];
            }
        }
        if(updateCallbackId ==-1)
        {
            if (cacheItemUpdatedListener != null) {
                short[] callabackIds = _eventManager.registerSelectiveEvent(cacheItemUpdatedListener, EnumSet.of(EventType.ItemUpdated), itemUpdateDataFilter);
                updateCallbackId = callabackIds[0];
            }
        }



        CacheItemVersion cVersion;
        cVersion = _cacheImpl.add(key, value, dependency, syncDependency,
                absoluteExpiration, slidingExpiration, priority, removeCallbackId, updateCallbackId,
                dsItemAddedCallbackId, isResyncExpiredItems, group, queryInfo, flagMap
                , providerName , itemUpdateDataFilter, itemRemovedDataFilter,
                size.argvalue, _cacheImpl.getEncryptionEnabled() , clientId, typeName);

        if (isPerfStatsCollectorInitialized()) {
            stats.EndSample();
            _perfStatsCollector.incrementMsecPerAddSample(stats.getCurrent());
            _perfStatsCollector.incrementAddPerSecStats();
        }
        return cVersion;
    }
    ///endregion

    ///endregion

    ///#region Insert Operations
    @Override
    public CacheItemVersion insert(String key, Object value) throws CacheException, java.lang.IllegalArgumentException {
        validateKeyValue(key,value);
        Expiration expiration = new Expiration(ExpirationType.DefaultAbsolute);
        CacheItem cacheItem = new CacheItem(value);
        cacheItem.setExpiration(expiration);
        return insert(key, cacheItem);
    }

    @Override

    public CacheItemVersion insert(String key, CacheItem item) throws CacheException, IllegalArgumentException {
        return insert(key, item,  new LockHandle(), false);
    }


    public CacheItemVersion insert(String key, CacheItem item,  LockHandle lockHandle, boolean releaseLock) throws CacheException, java.lang.IllegalArgumentException {
        return InsertInternal(key, item,  lockHandle, releaseLock);
    }

    private CacheItemVersion InsertInternal(String key, CacheItem item, LockHandle lockHandle, boolean releaseLock)
            throws CacheException, java.lang.IllegalArgumentException {

        validateKeyValue(key,item);
        String providerName = null;

        EnumSet eventTypes = EnumSet.of(EventTypeInternal.None);

        tangible.RefObject tempRef_providerName = new tangible.RefObject(providerName);
        RefObject> tempRef_eventTypes = new RefObject(eventTypes);
        providerName = tempRef_providerName.argvalue;

        eventTypes = tempRef_eventTypes.argvalue;
        //Consider only first element from enumset. For now multiple events are not supported.
        EventTypeInternal eventTypeInternal = (EventTypeInternal) eventTypes.toArray()[0];

        CacheItemVersion itemVersion = item.getCacheItemVersion();
        LockAccessType accessType = (itemVersion == null) ? LockAccessType.IGNORE_LOCK : LockAccessType.COMPARE_VERSION;

        if (lockHandle != null)
        {
            accessType = releaseLock ? LockAccessType.RELEASE : LockAccessType.DONT_RELEASE;
            itemVersion = null;
        }

        boolean ResyncOnExpirationEnable= false;


        return (CacheItemVersion) insertOperation(key, item.getValue(Object.class), item.getDependency(), item.getSyncDependency(), ConversionUtil.getAbsoluteExpiration(item.getExpiration()),
                ConversionUtil.getSlidingExpiration(item.getExpiration()), item.getCacheItemPriority(),
                 eventTypeInternal,
                ResyncOnExpirationEnable, item.getGroup(),  lockHandle, item.getCacheItemVersion(), accessType,
                CacheItemWrapperInternal.getCacheItemUpdatedListener(item), CacheItemWrapperInternal.getCacheItemRemovedListener(item),
                CacheItemWrapperInternal.getItemUpdatedDataFilter(item), CacheItemWrapperInternal.getItemRemovedDataFilter(item),
                true, null, (short) -1, (short) -1);
    }

//    @Override
//    public FutureTask insertAsync(String key, Object value) throws OperationFailedException {
//        validateKeyValue(key, value);
//        Expiration expiration = new Expiration(ExpirationType.DefaultAbsolute);
//        CacheItem cacheItem = new CacheItem(value);
//        cacheItem.setExpiration(expiration);
//        if (value instanceof DistributedDataStructure) {
//            throw new java.lang.IllegalArgumentException("Value cannot be a distributed data type.");
//        }
//        return  insertAsync(key, cacheItem);
//    }

//    @Override
//    public FutureTask insertAsync(String key, CacheItem item) throws OperationFailedException {
//        return insertAsync(key, item, new WriteThruOptions(WriteMode.None, ""));
//    }
//
//    @Override
//    public FutureTask insertAsync(String key, CacheItem item, WriteThruOptions writeThruOptions) throws OperationFailedException {
//        if (isNullOrEmpty(key)) {
//            throw new OperationFailedException(ErrorCodes.Common.EMPTY_KEY, ErrorMessages.getErrorMessage(ErrorCodes.Common.EMPTY_KEY));
//        }
//
//        if (item == null) {
//            throw new java.lang.IllegalArgumentException("Value cannot be null."+System.lineSeparator()+"Parameter name: CacheItem");
//        }
//        InsertAsyncCallable task = new InsertAsyncCallable(key, item, writeThruOptions, null, false);
//        FutureTask futureTask = new FutureTask(task);
//        ThreadPool.getInstance().executeTask(futureTask);
//        return futureTask;
//    }
    CacheItemVersion insertOperation(String key, Object value, CacheDependency dependency, CacheSyncDependency syncDependency, Date absoluteExpiration,
                                               TimeSpan slidingExpiration, CacheItemPriority priority,
                                                EventTypeInternal eventType, boolean isResyncExpiredItems, String group,
                                               LockHandle lockHandle, CacheItemVersion version,
                                               LockAccessType accessType,
                                               CacheDataModificationListener cacheItemUpdated, CacheDataModificationListener cacheItemRemoved,
                                               EventDataFilter itemUpdateDataFilter, EventDataFilter itemRemovedDataFilter
            , boolean allowQueryTags, String clientId, short updateCallbackId, short removeCallbackId)
            throws CacheException, java.lang.IllegalArgumentException {
        if (_cacheImpl == null) {
            throw new OperationFailedException(ErrorCodes.CacheInit.CACHE_NOT_INIT, ErrorMessages.getErrorMessage(ErrorCodes.CacheInit.CACHE_NOT_INIT));
        }
        validateKeyValue(key, value);

        String typeName = null;
        java.util.HashMap queryInfo = null;
        Alachisoft.NCache.Common.BitSet flagMap = new BitSet();
        Long size = (long) 0;
        //client cache item
        if (value instanceof ClientCacheItem) {
            ClientCacheItem clientcacheItem = (ClientCacheItem) ((value instanceof ClientCacheItem) ? value : null);
            value = clientcacheItem.getValue();
            queryInfo = clientcacheItem.getQueryInfo();

            if (clientcacheItem.getFlags().IsAnyBitSet((byte) BitSetConstants.BinaryData)) {
                flagMap.SetBit((byte) BitSetConstants.BinaryData);
            } else if (clientcacheItem.getFlags().IsAnyBitSet((byte) BitSetConstants.JsonData)) {
                flagMap.SetBit((byte) BitSetConstants.JsonData);
            }
            typeName = clientcacheItem.getGroupDataType();
        }

        //set defaults



        String lockId = (lockHandle == null) ? null : lockHandle.getLockId();
        if(itemUpdateDataFilter == null){
            itemUpdateDataFilter = EventDataFilter.None;
        }
        if(itemRemovedDataFilter == null){
            itemRemovedDataFilter = EventDataFilter.None;
        }
        UsageStats stats = new UsageStats();
        stats.BeginSample();

        //populate queryinfo
        if (queryInfo == null ) {
            queryInfo = new HashMap();
            if (allowQueryTags) {

                queryInfo.put("query-info", getQueryInfo(value));

            }
        }
        if (!isNullOrEmpty(group) && isNullOrEmpty(typeName))
        {
            typeName = getTypeName(value);
        }

        value = safeSerialize(value, _serializationContext, flagMap, size, UserObjectType.CacheItem);


        //accessType = releaseLock ? LockAccessType.RELEASE : LockAccessType.DONT_RELEASE;
        if (!isNullOrEmpty(lockId)) {
            flagMap.SetBit((byte) BitSetConstants.LockedItem);
        } else {
            flagMap.UnsetBit((byte) BitSetConstants.LockedItem);
        }

        if (absoluteExpiration != null && absoluteExpiration.compareTo(com.alachisoft.ncache.client.internal.caching.CacheImpl.NoAbsoluteExpiration) != 0 ){
            try {
                absoluteExpiration = NCDateTime.getUTCDate(absoluteExpiration);
            } catch (ParseException e) {
                //ignore
            }
        }
        if(removeCallbackId ==-1 ) {
            if (cacheItemRemoved != null) {
                short[] callbackIds = _eventManager.registerSelectiveEvent(cacheItemRemoved, EnumSet.of(EventType.ItemRemoved), itemRemovedDataFilter);
                removeCallbackId = callbackIds[1];
            }
        }
        if(updateCallbackId ==-1 ) {
            if (cacheItemUpdated != null) {
                short[] callabackIds = _eventManager.registerSelectiveEvent(cacheItemUpdated, EnumSet.of(EventType.ItemUpdated), itemUpdateDataFilter);
                updateCallbackId = callabackIds[0];
            }
        }

        CacheItemVersion newVersion = null;
        newVersion = _cacheImpl.insert(key, value, dependency, syncDependency, absoluteExpiration, slidingExpiration,
                priority, removeCallbackId, updateCallbackId, isResyncExpiredItems,
                group, queryInfo, flagMap, lockId, version, accessType,
                itemUpdateDataFilter, itemRemovedDataFilter, size,_cacheImpl.getEncryptionEnabled(), clientId, typeName, ListenerType.PushBasedNotification);

        if (isPerfStatsCollectorInitialized()) {
            stats.EndSample();
            _perfStatsCollector.incrementMsecPerUpdSample(stats.getCurrent());
            _perfStatsCollector.incrementUpdPerSecStats();
        }
        return newVersion;
    }
    ///endregion

    ///region Get Operations
    @Override



    public  T get(String key,  Class cls) throws CacheException, IllegalArgumentException, ClassCastException {

        LockAccessType accessType = LockAccessType.IGNORE_LOCK;
        CacheItemVersion version = null;
        LockHandle lockHandle = null;


        return  getInternal(key, version,accessType,noLockExpiration,lockHandle, cls);
    }
    @Override
    public  T get(String key, boolean acquireLock, TimeSpan lockTimeout, LockHandle lockHandle, Class cls) throws AggregateException, SecurityException, GeneralFailureException, ConfigurationException, OperationFailedException, CommandException, StreamException, OperationNotSupportedException, LicensingException, StreamNotFoundException, StreamAlreadyLockedException, CacheException {
        if(lockHandle == null){
            throw new java.lang.IllegalArgumentException("Value cannot be null.\r\nParameter name: LockHandle");
        }

        LockAccessType accessType = acquireLock ? LockAccessType.ACQUIRE : LockAccessType.DONT_ACQUIRE;
        CacheItemVersion version = null;


        return getInternal(key, version,   accessType, lockTimeout, lockHandle, cls);
    }


//    public  T get(String key, CacheItemVersion version, ReadThruOptions readThruOptions, Class cls) throws CacheException, IllegalArgumentException, ClassCastException {
//        if (version == null)
//        {
//            throw new java.lang.IllegalArgumentException("Value cannot be null.\r\nParameter name: version");
//        }
//
//        if (readThruOptions == null)
//            readThruOptions = new ReadThruOptions(ReadMode.None);
//
//        LockHandle lockHandle = null;
//        LockAccessType accessType = LockAccessType.DEFAULT;
//        accessType = (version.getVersion() == 0 ? LockAccessType.GET_VERSION : LockAccessType.MATCH_VERSION);
//
//        return getInternal(key,  version, accessType, noLockExpiration, lockHandle, readThruOptions, cls);
//    }



     T getInternal(String key, CacheItemVersion version, LockAccessType accessType, TimeSpan lockTimeout, LockHandle lockHandle,  Class cls)
            throws CacheException {
        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));
        }

        validateKey(key);

        if ((version == null) && (accessType == LockAccessType.COMPARE_VERSION)) {
            throw new java.lang.IllegalArgumentException("Value cannot be null \nParameter name: version");
        }


        if (lockHandle == null)
        {
            lockHandle = new LockHandle();
        }

        if (version == null)
        {
            version = new CacheItemVersion();
        }


        CompressedValueEntry result = null;
        BitSet flagMap = new BitSet();
        UsageStats stats = new UsageStats();

        stats.BeginSample();

        result = _cacheImpl.get(key, flagMap, null, version, lockHandle, lockTimeout, accessType);

        if(lockHandle != null && !lockHandle.getLockDate().equals(NCDateTime.MinValue))
            lockHandle.setLockDate(new NCDateTime(lockHandle.getLockDate()).getLocalizedDate());

        if (isPerfStatsCollectorInitialized()) {
            stats.EndSample();
            _perfStatsCollector.incrementMsecPerGetSample(stats.getCurrent());
            _perfStatsCollector.incrementGetPerSecStats();
        }

        if (result != null && result.getValue() != null && result.getType() == EntryType.CacheItem) {
            if (result.getFlag().IsBitSet((byte) _compressed)) {
                result.setValue(Decompress((byte[]) result.getValue()));
            }

            if (isPerfStatsCollectorInitialized() && result.getValue() != null && result.getValue() instanceof byte[]) {
                _perfStatsCollector.incrementAvgItemSize(((byte[]) result.getValue()).length);
            }



            result.setValue(safeDeserialize(result.getValue(), _serializationContext, result.getFlag(), UserObjectType.CacheItem, cls));
            return (T) result.getValue();

        }

        return (T) CacheHelper.getSafeValue(CacheHelper.getObjectOrInitializedCollection(key, result.getType(), result.getValue(), this,cls));
    }



    public CacheItem getCacheItem(String key) throws CacheException, IllegalArgumentException {

        LockAccessType accessType = LockAccessType.IGNORE_LOCK;
        CacheItemVersion version = null;
        LockHandle lockHandle = null;

        return  getCacheItemInternal(key, version,accessType,noLockExpiration,lockHandle);
    }

    public CacheItem getCacheItem(String key, CacheItemVersion version) throws CacheException {
        if (version == null)
        {
            throw new java.lang.IllegalArgumentException("Value cannot be null.\r\nParameter name: version");
        }

        LockHandle lockHandle = null;

        LockAccessType accessType = LockAccessType.DEFAULT;
        accessType = (version.getVersion() == 0 ? LockAccessType.GET_VERSION : LockAccessType.MATCH_VERSION);



        return getCacheItemInternal(key,  version, accessType, noLockExpiration, lockHandle);
    }

    public CacheItem getCacheItem(String key, boolean acquireLock, TimeSpan lockTimeout, LockHandle lockHandle) throws CacheException {
        if(lockHandle == null){
            throw new java.lang.IllegalArgumentException("Value cannot be null.\r\nParameter name: LockHandle");
        }
        LockAccessType accessType = acquireLock ? LockAccessType.ACQUIRE : LockAccessType.DONT_ACQUIRE;
        CacheItemVersion version = null;

        return getCacheItemInternal(key,   version, accessType, lockTimeout, lockHandle);
    }

    CacheItem getCacheItemInternal(String key,  CacheItemVersion version, LockAccessType accessType, TimeSpan lockTimeout, LockHandle lockHandle) throws CacheException {
        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));
        }
        validateKey(key);

        if ((version == null ) && (accessType == LockAccessType.COMPARE_VERSION)) {
            throw new java.lang.IllegalArgumentException("Value cannot be null or empty.\nParameter name: version");
        }

        if (lockHandle == null)
        {
            lockHandle = new LockHandle();
        }

        if (version == null)
        {
            version = new CacheItemVersion();
        }

        try {
            CacheEntry entry = null;
            BitSet flagMap = new BitSet();
            CacheItem item = new CacheItem();
            UsageStats stats = new UsageStats();


            stats.BeginSample();

            Object value = _cacheImpl.getCacheItem(key, flagMap, null, version, lockHandle, lockTimeout, accessType);

            if(lockHandle != null && !lockHandle.getLockDate().equals(NCDateTime.MinValue))
                lockHandle.setLockDate(new NCDateTime(lockHandle.getLockDate()).getLocalizedDate());

            if (isPerfStatsCollectorInitialized()) {
                stats.EndSample();
                _perfStatsCollector.incrementMsecPerGetSample(stats.getCurrent());
                _perfStatsCollector.incrementGetPerSecStats();
            }

            if (value == null) {
                return null;
            }

            if (value instanceof CacheItem) {
                return getCacheItemFromBinaryCacheItem(key, (CacheItem) ((value instanceof CacheItem) ? value : null));
            }


            return getCacheItemFromEntry(key, (CacheEntry) value);

        }
        catch (CacheException e) {
            if (getExceptionEnabled()) {
                    throw e;
            }
        }
        catch (Exception e) {
            if (getExceptionEnabled()) {
                throw new CacheException(e.getMessage(), e.getCause());
            }
        }
        return null;
    }

    CacheItem getCacheItemFromEntry(String key, CacheEntry entry) throws CacheException {
        CacheItem item = new CacheItem();
        if (entry != null) {
            CacheItemWrapperInternal.setEntryType(item, entry.getType());
            CacheItemWrapperInternal.setFlagValue(item, entry.getFlag());
            item.setValue(entry.getValue());

            if (CacheItemWrapperInternal.getFlagValue(item).IsBitSet((byte) this._compressed)) {
                try {
                    item.setValue(Decompress((byte[]) entry.getValue()));
                } catch (Exception e) {
                    throw new CacheException(e.getMessage(), e.getCause());
                }
            }

            if (isPerfStatsCollectorInitialized() && item.getValue(Object.class) != null && item.getValue(Object.class) instanceof byte[]) {
                _perfStatsCollector.incrementAvgItemSize(((byte[]) item.getValue(Object.class)).length);
            }

            item.setCacheItemPriority(entry.getPriority());

            ExpirationHint hint = entry.getExpirationHint();



            Date absoluteExpiration = null;
            absoluteExpiration = NCDateTime.MaxValue;

            TimeSpan slidingExpiration = TimeSpan.Zero;

            try {
                RefObject refAbsoluteExpiration = new RefObject<>(absoluteExpiration);
                RefObject refSlidingExpiration = new RefObject<>(slidingExpiration);

                item.setDependency(Alachisoft.NCache.Caching.AutoExpiration.DependencyHelper.GetCacheDependency(hint,
                        refAbsoluteExpiration, refSlidingExpiration));
                absoluteExpiration=refAbsoluteExpiration.getValue();
                slidingExpiration=refSlidingExpiration.getValue();

            } catch (Exception e) {
                throw new CacheException(e.getMessage(), e.getCause());
            }

            try {
                Date absExp = absoluteExpiration == NCDateTime.MaxValue ? absoluteExpiration : new NCDateTime(absoluteExpiration).getLocalizedDate();
                item.setExpiration(ExpirationUtil.getExpiration(absExp, slidingExpiration));
            } catch (ParseException e) {
                throw new CacheException(e.getMessage(), e.getCause());
            }

            if (entry.getGroupInfo() != null) {
                item.setGroup(entry.getGroupInfo().getGroup());
            }

            item.setCacheItemVersion(new CacheItemVersion(entry.getVersion()));
            CacheItemWrapperInternal.setCreationTime(item, entry.getCreationTime());
            CacheItemWrapperInternal.setLastModifiedTime(item, entry.getLastModifiedTime());

            if (entry.getQueryInfo() != null) {
                if (entry.getQueryInfo().containsKey("tag-info")) {
                    HashMap tagInfo = (HashMap) ((entry.getQueryInfo().get("tag-info") instanceof Map) ? entry.getQueryInfo().get("tag-info") : null);
                    ArrayList tagsList = (ArrayList) ((tagInfo.get("tags-list") instanceof ArrayList) ? tagInfo.get("tags-list") : null);

                }


            }

            CacheItemWrapperInternal.setCacheInstance(item, this); // NOTE : This property SHOULD ALWAYS be set just before returning the cache item
            item.setValue(CacheHelper.getObjectOrDataTypeForCacheItem(key, entry.getType(), item.getValue(Object.class)));
            return item;
        }
        return null;
    }

    CacheItem getCacheItemFromBinaryCacheItem(String key, CacheItem binaryCacheItem) throws CacheException {
        if (binaryCacheItem == null) {
            return null;
        }
        if (CacheItemWrapperInternal.getFlagValue(binaryCacheItem).IsBitSet((byte)this._compressed))
        {
            try {
                binaryCacheItem.setValue(Decompress((byte[]) binaryCacheItem.getValue(Object.class)));
            } catch (Exception e) {
                throw new CacheException(e.getMessage(), e.getCause());
            }
        }

        if (isPerfStatsCollectorInitialized() && binaryCacheItem.getValue(Object.class) != null && binaryCacheItem.getValue(Object.class) instanceof byte[]) {
            _perfStatsCollector.incrementAvgItemSize(((byte[]) binaryCacheItem.getValue(Object.class)).length);
        }



        try {
            binaryCacheItem.setExpiration(ExpirationUtil.getExpiration(ConversionUtil.getAbsoluteExpiration(binaryCacheItem.getExpiration()), ConversionUtil.getSlidingExpiration(binaryCacheItem.getExpiration())));
        } catch (ParseException e) {
            throw new CacheException(e.getMessage(), e.getCause());
        }
        CacheItemWrapperInternal.setCacheInstance(binaryCacheItem, this);
        binaryCacheItem.setValue(CacheHelper.getObjectOrDataTypeForCacheItem(key, CacheItemWrapperInternal.GetCacheItemEntryType(binaryCacheItem), binaryCacheItem.getValue(Object.class)));
        return binaryCacheItem;

    }




    public Map getCacheItemBulk(Iterable keys) throws CacheException {
        if (keys == null) {
            throw new java.lang.IllegalArgumentException("Value cannot be null."+System.lineSeparator()+"Parameter name: keys");
        }


        CacheEntry entry = null;
        BitSet flagMap = new BitSet();
        CacheItem item = new CacheItem();
        UsageStats stats = new UsageStats();

        if (_cacheImpl == null) {
            throw new OperationFailedException(ErrorCodes.CacheInit.CACHE_NOT_INIT, ErrorMessages.getErrorMessage(ErrorCodes.CacheInit.CACHE_NOT_INIT));
        }

        String[] keysList = toArray(keys);
        if (keysList.length == 0) {
            throw new java.lang.IllegalArgumentException("There is no key present in keys array");
        }


        try {
            stats.BeginSample();

            HashMap result = new HashMap();

            Map map = _cacheImpl.getCacheItemBulk(keysList, flagMap);

            if (_perfStatsCollector != null) {
                stats.EndSample();
                _perfStatsCollector.incrementMsecPerGetSample(stats.getCurrent());
                _perfStatsCollector.incrementGetPerSecStats();
            }

            if (map != null) {
                for (Object object : map.entrySet()) {
                    Map.Entry iteratorEntry = (Map.Entry) object;
                    CacheItem binaryCacheItem = (CacheItem) ((iteratorEntry.getValue() instanceof CacheItem) ? iteratorEntry.getValue() : null);

                    if (binaryCacheItem != null) {
                        result.put((String) iteratorEntry.getKey(), getCacheItemFromBinaryCacheItem((String) iteratorEntry.getKey(), binaryCacheItem));
                        continue;
                    }

                    result.put((String) iteratorEntry.getKey(), getCacheItemFromEntry((String) iteratorEntry.getKey(), (CacheEntry) ((iteratorEntry.getValue() instanceof CacheEntry) ? iteratorEntry.getValue() : null)));

                }
            }

            return result;
        } catch (Exception e) {
            if (getExceptionEnabled()) {
                throw new CacheException(e.getMessage(), e.getCause());
            }
        }
        return null;
    }



    //endregion

    ///region remove operations
    @Override

    public void delete(String key) throws CacheException, IllegalArgumentException {
        delete(key, null, null);
    }

    @Override
    public void delete(String key, LockHandle lockHandle) throws CacheException, IllegalArgumentException {
        delete(key,lockHandle,null);
    }


    public void delete(String key, LockHandle lockHandle, CacheItemVersion version) throws CacheException {
        String providerName = null;

        EnumSet eventTypes = EnumSet.of(EventTypeInternal.None);
        RefObject tempRef_providerName = new RefObject(providerName);
        RefObject> tempRef_eventTypes = new RefObject(eventTypes);
        providerName = tempRef_providerName.argvalue;

        eventTypes = tempRef_eventTypes.argvalue;
        //Consider only first element from enumset. For now multiple events are not supported.
        EventTypeInternal eventTypeInternal = (EventTypeInternal) eventTypes.toArray()[0];

        LockAccessType accessType = LockAccessType.IGNORE_LOCK;

        if (version != null)
        {
            accessType = LockAccessType.COMPARE_VERSION;
        }

        if (lockHandle != null)
        {
            accessType = LockAccessType.DEFAULT;
            version = null;
        }
        deleteInternal(key,  eventTypeInternal, lockHandle, version, accessType , providerName);
    }

    @Override

    public  T remove(String key, Class cls) throws CacheException, IllegalArgumentException,ClassCastException {
        return (T) remove(key, null, null, cls);

    }

    @Override
    public  T remove(String key, LockHandle lockHandle, Class cls) throws CacheException, IllegalArgumentException, ClassCastException {
        return (T) remove(key, lockHandle, null, cls);
    }


    public  T remove(String key, LockHandle lockHandle, CacheItemVersion version,  Class cls) throws CacheException {
        LockAccessType accessType = LockAccessType.IGNORE_LOCK;

        if (version != null)
        {
            accessType = LockAccessType.COMPARE_VERSION;
        }

        if (lockHandle != null)
        {
            accessType = LockAccessType.DEFAULT;
            version = null;
        }
        return (T) removeInternal(key,  lockHandle, version, accessType, cls);
    }

    private Object removeInternal(String key,  LockHandle lockHandle, CacheItemVersion itemVersion, LockAccessType accessType,  Class cls)
            throws CacheException {

        if (_cacheImpl == null) {
            throw new OperationFailedException(ErrorCodes.CacheInit.CACHE_NOT_INIT, ErrorMessages.getErrorMessage(ErrorCodes.CacheInit.CACHE_NOT_INIT));
        }
        validateKey(key);

        String providerName = null;

        EnumSet eventTypes = EnumSet.of(EventTypeInternal.None);

        RefObject tempRef_providerName = new RefObject(providerName);
        RefObject> tempRef_eventTypes = new RefObject(eventTypes);
        providerName = tempRef_providerName.argvalue;
        eventTypes = tempRef_eventTypes.argvalue;
        //Consider only first element from enumset. For now multiple events are not supported.
        EventTypeInternal eventTypeInternal = (EventTypeInternal) eventTypes.toArray()[0];


        UsageStats stats = new UsageStats();
        stats.BeginSample();

        String lockId = null;
        long version = 0;

        if (lockHandle != null) {
            lockId = lockHandle.getLockId();
        }

        if (itemVersion != null) {
            version = itemVersion.getVersion();
        }
        Alachisoft.NCache.Common.BitSet flagMap = new Alachisoft.NCache.Common.BitSet();

        short onDSItemRemovedCallbackId = -1;


        long objectSize = 0;
        long encryptedObjectSize = 0;
        long compressedObjectSize = 0;

        Object obj = null;
        CompressedValueEntry result = _cacheImpl.remove(key, flagMap, onDSItemRemovedCallbackId, lockId, HelperUtil.createCacheItemVersion(version), accessType, providerName);
        if (result != null && result.getValue() != null) {

            if (result.getFlag().IsBitSet((byte) BitSetConstants.Compressed) == true) {
                result.setValue(Decompress((byte[]) result.getValue()));
            }

            if (isPerfStatsCollectorInitialized() && result.getValue() != null && result.getValue() instanceof byte[]) {
                _perfStatsCollector.incrementAvgItemSize(((byte[]) result.getValue()).length);
            }



            obj = safeDeserialize(result.value, _serializationContext, result.getFlag(), UserObjectType.CacheItem, cls);
        }
        if (isPerfStatsCollectorInitialized()) {
            stats.EndSample();
            _perfStatsCollector.incrementMsecPerDelSample(stats.getCurrent());
            _perfStatsCollector.incrementDelPerSecStats();
        }

        return obj;
    }

    private void deleteInternal(String key, EventTypeInternal eventType , LockHandle lockHandle, CacheItemVersion itemVersion, LockAccessType accessType , String providerName)
            throws CacheException {
        if (_cacheImpl == null)
        {
            throw new OperationFailedException(ErrorCodes.CacheInit.CACHE_NOT_INIT, ErrorMessages.getErrorMessage(ErrorCodes.CacheInit.CACHE_NOT_INIT));
        }
        validateKey(key);
        Alachisoft.NCache.Common.BitSet flagMap = new Alachisoft.NCache.Common.BitSet();


        UsageStats stats = new UsageStats();
        stats.BeginSample();
        String lockId = (lockHandle == null) ? null : lockHandle.getLockId();


        short dsItemRemovedCallbackId = -1;



        _cacheImpl.delete(key, flagMap, dsItemRemovedCallbackId, lockId, itemVersion, accessType, providerName);

        if (_perfStatsCollector != null)
        {
            stats.EndSample();
            _perfStatsCollector.incrementMsecPerDelSample(stats.getCurrent());
            _perfStatsCollector.incrementDelPerSecStats();
        }
    }



//    @Override
//    public FutureTask deleteAsync(String key) {
//        return deleteAsync(key, null);
//    }
//
//    @Override
//    public FutureTask deleteAsync(String key, WriteThruOptions writeThruOptions) {
//        DeleteAsyncCallable task = new DeleteAsyncCallable(key, writeThruOptions);
//        FutureTask futureTask = new FutureTask(task);
//        ThreadPool.getInstance().executeTask(futureTask);
//        return futureTask;
//    }

    //endregion

    ///endregion


    // region Notification Service
    void addCacheStoppedListener(CacheStatusEventListener cacheStopped) throws CacheException {
        cacheStoppedListeners.add(cacheStopped);
        if (_cacheImpl != null && ++_refCacheStoppedCount == 1) {
            _cacheImpl.registerCacheStoppedEvent();
        }

    }

    void removeCacheStoppedListener(CacheStatusEventListener cacheStopped) throws CacheException {
        cacheStoppedListeners.add(cacheStopped);
        if (_cacheImpl != null && ++_refCacheStoppedCount == 1) {
            _cacheImpl.unregisterCacheStoppedEvent();
        }
    }

    void addCacheClearedListner(CacheClearedListener cacheClearedListener) throws CacheException {
        cacheClearedListeners.add(cacheClearedListener);
        if (_cacheImpl != null && ++_refClearCount == 1) {
            _cacheImpl.registerClearEvent();
        }
    }

    void removeCacheClearedListner(CacheClearedListener cacheClearedListener) throws CacheException {
        int beforeLength, afterLength = 0;
        synchronized (this) {
            if (cacheClearedListeners != null) {
                beforeLength = cacheClearedListeners.size();
                cacheClearedListeners.remove(cacheClearedListener);

                if (cacheClearedListeners != null) {
                    afterLength = cacheClearedListeners.size();
                }

                if (beforeLength - afterLength == 1) {
                    if (_cacheImpl != null && --_refClearCount == 0) {
                        _cacheImpl.unregisterClearEvent();
                    }
                }
            }
        }
    }

    void addMemberJoinedListener(CacheStatusEventListener memberJoinedListener) {
        memberJoinedListeners.add(memberJoinedListener);
    }

    void removeMemberJoinedListener(CacheStatusEventListener memberJoinedListener) {
        memberJoinedListeners.remove(memberJoinedListener);
    }

    void addMemberLeftListener(CacheStatusEventListener memberLeftListener) {
        memberLeftListeners.add(memberLeftListener);
    }
    void removeMemberLeftListener(CacheStatusEventListener memberLeftListener) {
        memberLeftListeners.remove(memberLeftListener);
    }





    public CacheEventDescriptor registerCacheNotificationInternal(String key, CacheDataModificationListener listener, EnumSet eventTypes, EventDataFilter datafilter, boolean notifyOnItemExpiration) throws CacheException {
        return registerCacheNotificationInternal(key, listener, eventTypes, datafilter, notifyOnItemExpiration, ListenerType.PushBasedNotification);
    }

    public CacheEventDescriptor registerCacheNotificationInternal(String key, CacheDataModificationListener listener, EnumSet eventTypes, EventDataFilter datafilter, boolean notifyOnItemExpiration, ListenerType listenerType) throws CacheException {
        if (_cacheImpl == null) {
            throw new OperationFailedException(ErrorCodes.CacheInit.CACHE_NOT_INIT, ErrorMessages.getErrorMessage(ErrorCodes.CacheInit.CACHE_NOT_INIT));
        }

        CacheEventDescriptor discriptor = null;

        try {
            if (key != null) {
                short[] callbackRefs = _eventManager.registerSelectiveEvent(listener, eventTypes, datafilter, listenerType);
                _cacheImpl.registerKeyNotificationListener(key, callbackRefs[0], callbackRefs[1], datafilter, notifyOnItemExpiration, listenerType);
            } else {
                discriptor = _eventManager.registerGeneralEvents(listener, eventTypes, datafilter);
            }
        } catch (Exception ex) {
            if (getExceptionEnabled()) {
                throw new CacheException(ex.getMessage(), ex.getCause());
            }
        }
        return discriptor;
    }



    // region Notifiaction Async Callable


    void addClientConnectivityChangedListener(CacheClientConnectivityChangedListener clientConnectivityChangedListener) throws CacheException {
        boolean register = false;
        synchronized (this) {
            cacheClientConnectivityChangedListeners.add(clientConnectivityChangedListener);
            //registration is required only when first callback is added
            register = cacheClientConnectivityChangedListeners.size() == 1;
        }

        if (_cacheImpl != null && register) {
            _cacheImpl.registerCacheClientConnectivityEvent();
        }
    }

    void removeClientConnectivityChangedListener(CacheClientConnectivityChangedListener cacheClientConnectivityChangedListener) throws CacheException {
        boolean unregister = false;
        synchronized (this) {
            cacheClientConnectivityChangedListeners.remove(cacheClientConnectivityChangedListener);
            //unregister as there are no more callbacks registered
            unregister = cacheClientConnectivityChangedListeners == null || cacheClientConnectivityChangedListeners.size() == 0;
        }

        if (_cacheImpl != null && unregister) {
            _cacheImpl.unregisterCacheClientConnectivityEvent();
        }
    }

    // endregion

    //endregion

    ///endregion

    ///region internal methods

    //region Private Methods


    private String[] removeDuplicateKeys(Iterable keys) {
        HashSet keysSet = new HashSet();
        for(String key :keys)
        {
            if (key!= null) {
                keysSet.add(key);
            } else {
                throw new java.lang.IllegalArgumentException("Value cannot be null."+System.lineSeparator()+"Parameter name: key");
            }
        }
        String[] array = keysSet.toArray(new String[keysSet.size()]);
        return array;
    }

    private void loadRWTrhuSettings() throws CacheException {
        ClientConfiguration config = new ClientConfiguration(this._cacheId);
        config.loadConfiguration();
    }

    void removeGroupData(String group) throws CacheException {
        if (_cacheImpl == null) {
            throw new OperationFailedException(ErrorCodes.CacheInit.CACHE_NOT_INIT, ErrorMessages.getErrorMessage(ErrorCodes.CacheInit.CACHE_NOT_INIT));
        }

        if (group == null) {
            throw new java.lang.IllegalArgumentException("Value cannot be null."+System.lineSeparator()+"Parameter name: group");
        }

        try {
            _cacheImpl.remove(group);
        } catch (Exception e) {
            if (getExceptionEnabled()) {
                throw new CacheException(e.getMessage(), e.getCause());
            }
        }
        return;
    }

    ArrayList getGroupKeys(String group) throws CacheException {
        if (_cacheImpl == null) {
            throw new OperationFailedException(ErrorCodes.CacheInit.CACHE_NOT_INIT, ErrorMessages.getErrorMessage(ErrorCodes.CacheInit.CACHE_NOT_INIT));
        }

        if (group == null) {
            throw new java.lang.IllegalArgumentException("Value cannot be null."+System.lineSeparator()+"Parameter name: group");
        }

        try {
            return (ArrayList) _cacheImpl.getGroupKeys(group);
        } catch (Exception e) {
            if (getExceptionEnabled()) {
                throw new CacheException(e.getMessage(), e.getCause());
            }
        }
        return null;
    }

     Map getGroupData(String group) throws CacheException {
        if (_cacheImpl == null) {
            throw new OperationFailedException(ErrorCodes.CacheInit.CACHE_NOT_INIT, ErrorMessages.getErrorMessage(ErrorCodes.CacheInit.CACHE_NOT_INIT));
        }

        if (group == null) {
            throw new java.lang.IllegalArgumentException("Value cannot be null."+System.lineSeparator()+"Parameter name: group");
        }

        try {
            UsageStats stats = new UsageStats();

            stats.BeginSample();

            Map groupResult = new HashMap<>();
            HashMap data = _cacheImpl.getGroupData(group);

            if (data != null) {
                Iterator> ie= data.entrySet().iterator();
                while (ie.hasNext())
                {
                    Map.Entry entry=ie.next();
                    CompressedValueEntry result =entry.getValue();

                    if (result.getFlag().IsBitSet((byte) _compressed)) {
                        result.setValue(Decompress((byte[]) result.getValue()));
                    }

                    if (isPerfStatsCollectorInitialized() && result.getValue() != null && result.getValue() instanceof byte[]) {
                        _perfStatsCollector.incrementAvgItemSize(((byte[]) result.getValue()).length);
                    }


                    groupResult.put(entry.getKey(), safeDeserialize(result.getValue(), _serializationContext, result.getFlag(), UserObjectType.CacheItem, null));
                }

                if (isPerfStatsCollectorInitialized()) {
                    stats.EndSample();
                    _perfStatsCollector.incrementMsecPerGetBulkSample(stats.getCurrent());
                    _perfStatsCollector.incrementByGetPerSecStats(data.size());
                }

            }
            return groupResult;
        } catch (Exception e) {
            if (getExceptionEnabled()) {
                throw new CacheException(e.getMessage(), e.getCause());
            }
        }
        return null;
    }

    //endregion

    private void validateKey(String key) throws OperationFailedException {
        if (key == null) {
            throw new IllegalArgumentException("Value cannot be null."+System.lineSeparator()+"Parameter name: key");
        }
        if (key.isEmpty()) {
            throw new OperationFailedException(ErrorCodes.Common.EMPTY_KEY, ErrorMessages.getErrorMessage(ErrorCodes.Common.EMPTY_KEY));
        }
    }

    private void validateKeyValue(String key, Object value) throws OperationFailedException {
        if (key == null) {
            throw new IllegalArgumentException("Value cannot be null."+System.lineSeparator()+"Parameter name: key");
        }
        if (key.isEmpty()) {
            throw new OperationFailedException(ErrorCodes.Common.EMPTY_KEY, ErrorMessages.getErrorMessage(ErrorCodes.Common.EMPTY_KEY));
        }
        if (value == null) {
            throw new IllegalArgumentException("Value cannot be null."+System.lineSeparator()+"Parameter name: value");
        }
    }

    public ArrayList getCustomEventList() {
        return null;
    }

    public void addRef() {
        synchronized (this) {
            _refCount.incrementAndGet();
        }
    }

    public boolean isInProc() {
        return _cacheImpl.isInproc();
    }

    protected void SetMessagingServiceCacheImpl(CacheImplBase cacheImpl) {
        _messagingService.getPubSubManager().setCacheImpl(cacheImpl);
    }



    private HashMap getPortability() {
        return SerializationUtil.getPortability(_cacheId.toLowerCase());
    }

    private HashMap getAttributeOrder() {
        return SerializationUtil.getAttributeOrder(_cacheId.toLowerCase());
    }

    public void initializeCompactFramework() throws NCacheArgumentException, OperationFailedException {
        impl = FormatterServices.getDefault();

        // get User assigned Classes to be Dynamically Serialize
        Map type = _cacheImpl.getCompactTypes();
        if (type != null) {
            if (!type.isEmpty()) {
                impl.register(type, getAttributeOrder(), this._cacheId.toLowerCase(), getPortability());

            }
        }


        impl.registerKnownTypes(Alachisoft.NCache.Common.DataStructures.VirtualArray.class, (short) 149);
        impl.registerKnownTypes(Alachisoft.NCache.Common.DataStructures.NewHashmap.class, (short) 346);
        this._compressionEnabled = _cacheImpl.getCompressionEnabled();
        this._compressionThresholdSize = _cacheImpl.getCompressionThreshold();
    }

    public void addSecondaryInprocInstance(Cache secondaryInstance) {
        if (_secondaryInprocInstances == null) {
            _secondaryInprocInstances = new java.util.ArrayList();
        }

        _secondaryInprocInstances.add(secondaryInstance);
    }

    /// 
    /// this method is used from the sync cache to synchronize the L1 cache with L2.
    /// getting a serialized object reduces the cost as we need to put the same object again in the cache.
    /// 
    /// 
    /// 
    Object getSerializedObject(String key,  tangible.RefObject v, tangible.RefObject flag, tangible.RefObject absoluteExpiration, tangible.RefObject slidingExpiration,tangible.RefObject group, tangible.RefObject queryInfo
    , tangible.RefObject cacheDependency, tangible.RefObject priority) throws Exception {
        if (_cacheImpl == null) {
            throw new OperationFailedException(ErrorCodes.CacheInit.CACHE_NOT_INIT, ErrorMessages.getErrorMessage(ErrorCodes.CacheInit.CACHE_NOT_INIT));
        }

        if (key == null) {
            throw new IllegalArgumentException("Value cannot be null."+System.lineSeparator()+"Parameter name: key");
        }
        if (key.isEmpty()) {
            throw new OperationFailedException(ErrorCodes.Common.EMPTY_KEY, ErrorMessages.getErrorMessage(ErrorCodes.Common.EMPTY_KEY));
        }

        CacheItem result = null;
        try {
            BitSet flagMap = new BitSet();
            group.argvalue = null;
            queryInfo.argvalue = null;
            cacheDependency.argvalue = null;




            LockHandle lockHandle = new LockHandle();
            CacheItemVersion version = HelperUtil.createCacheItemVersion(0);
            RefObject tempRef_version = new RefObject<>(version);
            result = (CacheItem) _cacheImpl.getCacheItem(key, flagMap, null,  version, lockHandle, TimeSpan.Zero, LockAccessType.GET_VERSION);

            //muds:
            //set the version...
            if (version != null) {
                v.argvalue = version.getVersion();
            }
            if(result == null)
                return null;
            //+Asad: set the flag
            flag.argvalue.setData(result.getFlagMapInternal().getData());
            //flag.Data = result.Flag.Data;

            if (result != null && result.getValue(null) != null) {
                Object value = result.getValue(null);
                if (value instanceof CallbackEntry) {
                    CallbackEntry e = (value instanceof CallbackEntry) ? (CallbackEntry) value : null;
                    result.setValue((byte[]) e.getValue());
                }
                if (result.getFlagMapInternal().IsBitSet((byte) _compressed)) {
                    result.setValue(Decompress((byte[]) value));
                }


                if(result.getExpiration() != null){
                    absoluteExpiration.argvalue = ConversionUtil.getAbsoluteExpiration(result.getExpiration());
                    slidingExpiration.argvalue = ConversionUtil.getSlidingExpiration(result.getExpiration());
                }

                cacheDependency.argvalue = result.getDependency();

                priority.argvalue = result.getCacheItemPriority();
                v.argvalue = result.getCacheItemVersion().getVersion();

                if (isPerfStatsCollectorInitialized() && value != null && value instanceof byte[]) {
                    _perfStatsCollector.incrementAvgItemSize(((byte[]) value).length);
                }
                return result;

            }
        } catch (Exception e) {
            if (isExceptionsEnabled()) {
                throw e;
            } else {
                return null;
            }
        }
        return null;
    }

    public boolean isExceptionsEnabled() {
        return _exceptionEnabled;
    }

    public void setExceptionsEnabled(boolean exceptionsEnabled) {
        this._exceptionEnabled = exceptionsEnabled;
    }//

    boolean setAttributes(String key, CacheItemAttributes attributes) throws Exception {
        if (key == null) {
            throw new IllegalArgumentException("Value cannot be null."+System.lineSeparator()+"Parameter name: key");
        }

        if (attributes == null) {
            throw new IllegalArgumentException("Value cannot be null."+System.lineSeparator()+"Parameter name: attributes");
        }
        return _cacheImpl.setAttributes(key, attributes);
    }

    void makeTargetCacheActivePassive(boolean makeActive) throws Exception {
        try {
            _cacheImpl.makeTargetCacheActivePassive(makeActive);
        } catch (CacheException e) {
            if (_exceptionEnabled) {
                throw e;
            }
        }
    }

    public MessagingService getMessagingService() throws CacheException {
        return _messagingService;
    }

    String getTargetCacheUniqueID() {
        return _cacheImpl.getBridgeTargetCacheUniqueID();
    }

    void setQueryTypeInfoMap(TypeInfoMap typeMap) {
        this.typeMap = typeMap;
    }

    TypeInfoMap getQueryTypeMap() {
        return _cacheImpl != null ? _cacheImpl.getTypeMap() : null;
    }

    public CacheImplBase getCacheImpl() {
        return _cacheImpl;
    }

    public void setCacheImpl(CacheImplBase value) {
        _cacheImpl = value;
        if (_cacheImpl != null) {
            _serializationContext = _cacheImpl.getName();
            _cacheId = _serializationContext.toLowerCase();
        }
    }

    void setEncryptionEnabled(boolean _encryptionEnabled) {
        _cacheImpl.setEncryptionEnabled(_encryptionEnabled);//this._encryptionEnabled = _encryptionEnabled;
    }

    private void dispose()  {
        dispose(true);
    }

    private void dispose(boolean disposing) {
        synchronized (this) {
            _refCount.decrementAndGet();
            if (_refCount.get() > 0) {
                return;
            } else if (_refCount.get() < 0) {
                _refCount.set(0);
            }
            if (_messagingService != null) {
                _messagingService.dispose();
            }
            synchronized (CacheManager.getCaches()) {
                if (_cacheId != null) {
                    CacheManager.getCaches().removeCache(_cacheId);
                    SerializationUtil.unRegisterCache(_cacheId);
                    _cacheId = null; //bug 962 Set the cache id to null; this means that cache is disposed.
                }
            }


            if (_cacheImpl != null) {
                try {
                    _cacheImpl.dispose(disposing);
                } catch (GeneralFailureException | OperationFailedException | ConfigurationException e) {
                }
            }


            if (_secondaryInprocInstances != null) {
                for (Object cache : _secondaryInprocInstances) {
                    ((CacheImpl) cache).dispose();
                }
            }


            if (_clusterListener != null) {
                _clusterListener.dispose();
            }

            if (isPerfStatsCollectorInitialized())
                _perfStatsCollector.dispose();

            _cacheImpl = null;
            _messagingService = null;

            ThreadPool.getInstance().Stop(true);
        }
    }


    ///endregion

    ///endregion

    ///region helper methods

    public boolean checkUserAuthorization(String cacheId, String password, String userId) {
        try {
            return _cacheImpl.checkSecurityAuthorization(cacheId.toLowerCase(), Alachisoft.NCache.Common.EncryptionUtil.Encrypt(password), userId);
        } catch (Exception e) {
            return false;
        }
    }

    private void findNull(String[] keys) throws OperationFailedException {
        try {
            for (String item : keys) {
                if (item == null) {
                    throw new java.lang.IllegalArgumentException("keys cannot contain null values");
                }
            }

        } catch (java.lang.IllegalArgumentException e) {
            throw new OperationFailedException(e.getMessage());
        }
    }


    private String[] RemoveDuplicateKeys(String[] keys) {
        java.util.HashSet keysAndItems = new java.util.HashSet();
        for (int item = 0; item < keys.length; item++)
        {
            if (keys[item] != null)
            {
                keysAndItems.add(keys[item]);
            }
            else
            {
                throw new java.lang.IllegalArgumentException("Value cannot be null.\r\nParameter name: key");
            }
        }
        return toArray(keysAndItems);
    }


    //Can be accesed within package and in by the class itself. Not available to 'world' and 'subclasses'
    Object getDeserializedObject(Object value, String serializationContext, Alachisoft.NCache.Common.BitSet flag) throws GeneralFailureException {
        if (value instanceof byte[] && _cacheImpl.getSerializationEnabled()) {
            try {
                if (flag != null && flag.IsBitSet((byte) BitSetConstants.BinaryData)) {
                    return value;
                }

                UsageStats statsSerialization = new UsageStats();
                statsSerialization.BeginSample();

                value = _cacheImpl.safeDeserialize(value, serializationContext.toLowerCase(), flag, _cacheImpl,null, null);
                statsSerialization.EndSample();
                if (isPerfStatsCollectorInitialized()) {
                    _perfStatsCollector.incrementMsecPerDeserialization(statsSerialization.getCurrent());
                }
                return value;
            } catch (Exception iOException) {
                return value;
            }
        } else {
            return value;
        }
    }

    public Object safeSerialize(Object serializableObject, String serializationContext, BitSet flag, Long size, UserObjectType userObjectType) throws OperationFailedException, GeneralFailureException {
        return safeSerialize(serializableObject, serializationContext, flag, size, userObjectType, false);
    }
    public Object safeDataStructuresSerialize(Object serializableObject, String serializationContext, BitSet flag, Long size, UserObjectType userObjectType, boolean isCustomAttributeBaseSerialzed){
        try {
            return safeSerialize(serializableObject, serializationContext, flag,size, userObjectType, isCustomAttributeBaseSerialzed) ;
        } catch (OperationFailedException e) {
            throw new OperationFailedRuntimeException(e);
        }catch (GeneralFailureException e){
            throw new GeneralFailureRuntimeException(e);
        }
    }

    public Object safeSerialize(Object serializableObject, String serializationContext, BitSet flag,  Long size, UserObjectType userObjectType, boolean isCustomAttributeBaseSerialzed) throws OperationFailedException, GeneralFailureException {
        if (!_serializationEnabled) {
            return serializableObject;
        }

        Object serializedObject = null;

        if (_cacheImpl == null)
            throw new OperationFailedException(ErrorCodes.CacheInit.CACHE_NOT_INIT, ErrorMessages.getErrorMessage(ErrorCodes.CacheInit.CACHE_NOT_INIT));

        if (serializableObject != null) {
            UsageStats statsSerialization = new UsageStats();
            statsSerialization.BeginSample();
            RefObject  temp_size=new RefObject<>(size);
            serializedObject = _cacheImpl.safeSerialize(serializableObject, serializationContext, flag, _cacheImpl,temp_size, userObjectType, isCustomAttributeBaseSerialzed);
            statsSerialization.EndSample();
            if (isPerfStatsCollectorInitialized()) {
                _perfStatsCollector.incrementMsecPerSerialization(statsSerialization.getCurrent());
            }
        }

        return serializedObject;
    }

    public  T safeDataStructuresDeserialize(Object serializedObject, String serializationContext, BitSet flag, UserObjectType userObjectType,Class cls)
    {
        try {
            return safeDeserialize(serializedObject,serializationContext,flag,userObjectType, cls);
        } catch (OperationFailedException e) {
            throw new OperationFailedRuntimeException(e);
        }
    }
    public  T safeDeserialize(Object serializedObject, String serializationContext, BitSet flag, UserObjectType userObjectType, Class cls) throws OperationFailedException{
        if (!getSerializationEnabled()) {
            return (T) serializedObject;
        }

        T deSerializedObject = null;

        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));
        }

        if (serializedObject != null) {
            UsageStats statsDeserialization = new UsageStats();
            statsDeserialization.BeginSample();
            deSerializedObject = _cacheImpl.safeDeserialize(serializedObject, serializationContext, flag, _cacheImpl, userObjectType, cls);
            statsDeserialization.EndSample();
            if (isPerfStatsCollectorInitialized()) {
                _perfStatsCollector.incrementMsecPerDeserialization(statsDeserialization.getCurrent());
            }
        }

        return deSerializedObject;

    }



    private void setExpirationTimeInUtc(RefObject absoluteExpiration) {
        if (absoluteExpiration.getValue() != null && absoluteExpiration.getValue() != com.alachisoft.ncache.client.internal.caching.CacheImpl.NoAbsoluteExpiration) {
            try {
                absoluteExpiration.setValue(NCDateTime.getUTCDate(absoluteExpiration.getValue()));
            } catch (ParseException e) {
                //ignore
            }
        }
    }

    private void setDefaults( RefObject refproviderName, RefObject refresyncProviderName) {


        if (!tangible.DotNetToJavaStringHelper.isNullOrEmpty(refproviderName.getValue())) {
            refproviderName.setValue(refproviderName.getValue().toLowerCase());
        }
        if (!tangible.DotNetToJavaStringHelper.isNullOrEmpty(refresyncProviderName.getValue())) {
            refresyncProviderName.setValue(refresyncProviderName.getValue().toLowerCase());
        }

    }

    private boolean populateFromClientCacheItem(RefObject typeName, RefObject htQueryInfo, RefObject flagMap, RefObject valueWrapper) {

        if (valueWrapper.getValue() instanceof ClientCacheItem) {
            ClientCacheItem clientcacheItem = (ClientCacheItem) ((valueWrapper.getValue() instanceof ClientCacheItem) ? valueWrapper.getValue() : null);
            valueWrapper.setValue(clientcacheItem.getValue());
            htQueryInfo.setValue(clientcacheItem.getQueryInfo());

            if (clientcacheItem.getFlags().IsAnyBitSet((byte) BitSetConstants.BinaryData)) {
                flagMap.getValue().SetBit((byte) BitSetConstants.BinaryData);
            } else if (clientcacheItem.getFlags().IsAnyBitSet((byte) BitSetConstants.JsonData)) {
                flagMap.getValue().SetBit((byte) BitSetConstants.JsonData);
            }
            typeName.setValue(clientcacheItem.getGroupDataType());
            return true;
        }
        return false;
    }







    //endregion


    protected long getCompressionThreshold() {

        return _compressionThresholdSize;

    }

    private byte[] Compress(byte[] value, Alachisoft.NCache.Common.BitSet flag, long threshold) {
        UsageStats statsCompression = new UsageStats();
        statsCompression.BeginSample();
        byte[] result = CompressionUtil.Compress(value, flag, threshold);
        statsCompression.EndSample();
        if (isPerfStatsCollectorInitialized()) {
            _perfStatsCollector.incrementMsecPerCompression(statsCompression.getCurrent());
            _perfStatsCollector.incrementAvgCompressedItemSize(result != null ? result.length : 0);
            if (flag != null && flag.IsAnyBitSet((byte) BitSetConstants.Compressed)) {
                _perfStatsCollector.incrementCompressionPerSec();
            }

        }
        return result;
    }

    private byte[] Decompress(byte[] value) {
        if (isPerfStatsCollectorInitialized() && value != null) {
            _perfStatsCollector.incrementAvgCompressedItemSize(value.length);
        }
        UsageStats statsCompression = new UsageStats();
        statsCompression.BeginSample();
        byte[] decompressedValue = new byte[0];
        try {
            decompressedValue = CompressionUtil.Decompress(value);
        } catch (IOException e) {
            //ignore
        }
        statsCompression.EndSample();
        if (isPerfStatsCollectorInitialized()) {
            _perfStatsCollector.incrementMsecPerDecompression(statsCompression.getCurrent());
            _perfStatsCollector.incrementCompressionPerSec();
        }
        return decompressedValue;
    }

    protected boolean getCompressionEnabled() {
        return this._compressionEnabled;
    }

    protected boolean getSerializationEnabled() {
        return true;
    }

    boolean isPerfStatsCollectorInitialized() {
        return _perfStatsCollector != null;
    }

    private boolean equalsIgnoreCase(ArrayList tagsList,String tag)
    {
        for(String tagg :tagsList)
        {
            if(tagg.equalsIgnoreCase(tag))
                return true;
        }
        return false;
    }



    private String getTypeName(Object value){
        String typeName = value.getClass().getCanonicalName();
        if(JsonObject.class.isAssignableFrom(value.getClass())) {
            if (!isNullOrEmpty(((JsonObject) value).getType()))
                typeName = ((JsonObject) value).getType();
        }
        typeName = typeName.replace("+", ".");
        return typeName;
    }

    HashMap getQueryInfo(Object value) throws GeneralFailureException {

        HashMap queryInfo = null;

        TypeInfoMap map = this.typeMap != null ? this.typeMap : _cacheImpl.getTypeMap();

        if (map == null) {
            return null;
        } else {
            this.typeMap = map;
        }

        try {
            String typeName= value.getClass().getCanonicalName();
            boolean isJson=false;

            if(JsonObject.class.isAssignableFrom(value.getClass()))
            {
                if(!isNullOrEmpty(((JsonObject) value).getType()))
                {
                    typeName=((JsonObject) value).getType();
                    isJson=true;
                }
            }
            int handleId = typeMap.getHandleId(typeName);
            if (handleId != -1) {
                queryInfo = new HashMap();
                ArrayList attribValues = new ArrayList();
                ArrayList attributes = typeMap.getAttribList(handleId);

                for (int i = 0; i < attributes.size(); i++) {
                    if(!isJson) {
                        Field fieldAttrib = value.getClass().getDeclaredField((String) attributes.get(i));
                        if (fieldAttrib != null) {
                            fieldAttrib.setAccessible(true);
                            Object attribValue = fieldAttrib.get(value);

                            attribValues.add(attribValue);
                        } else {
                            throw new Exception("Unable to extract query information from user object.");
                        }
                    }
                    else
                    {
                        JsonObject jsonObject= (JsonObject)value;
                        JsonValue jsonValue= (JsonValue) jsonObject.getAttributeValue((String)attributes.get(i));
                        if(jsonValue!=null)
                        {
                            attribValues.add(jsonValue.getValue());
                        }
                        else
                            throw new Exception ("Unable to extract query information from user object for attribute \""+(String)attributes.get(i)+"\"");
                    }
                }
                queryInfo.put(handleId, attribValues);
            }

        } catch (Exception e) {
            throw new GeneralFailureException(e.getMessage());
        }

        return queryInfo;
    }
    ///endregion
    //endregion


    ///region Bulk Operations
    @Override
    public  Map getBulk(Iterable keys, Class cls) throws CacheException {
        return getBulkInternal(keys,  cls);
    }

    private  Map getBulkInternal(Iterable keys,  Class cls) throws OperationFailedException, AggregateException, SecurityException, GeneralFailureException, ConfigurationException,CacheException {
        UsageStats stats = new UsageStats();
        if (_cacheImpl == null) {
            throw new OperationFailedException("Cache is not initialized");
        }
        if (keys == null) {
            throw new java.lang.IllegalArgumentException("keys");
        }
        String[] keysArr= toArray(keys);
        if (keysArr.length == 0) {
            throw new java.lang.IllegalArgumentException("There is no key present in keys array");
        }

        keysArr = RemoveDuplicateKeys(keysArr);

        Alachisoft.NCache.Common.BitSet flagMap = new Alachisoft.NCache.Common.BitSet();

        HashMap resultMap = null;


        try {

            resultMap = (HashMap) _cacheImpl.get(keysArr, flagMap);
            if (resultMap == null) {
                return null;
            }
            //Usama: Variables used for size info for APILogging
            long sumObjectSize = 0;
            long sumCompressedObjectSize = 0;
            long sumEncryptedObjectSize = 0;

            HashMap deflatMap = new HashMap(resultMap.size());
            ///Do decompression (if necessary) and deserialization of data
            ///and add to a new table
            for (Object entry : resultMap.entrySet()) {
                if (Common.is(((Map.Entry) entry).getValue(), CompressedValueEntry.class)) {
                    Map.Entry pair
                            = (Map.Entry) entry;
                    CompressedValueEntry result = pair.getValue();
                    if (result != null && result.getType() == EntryType.CacheItem) {
                        if (result.getValue() instanceof byte[]) {
                            if (result.getFlag().IsBitSet((byte) BitSetConstants.Compressed) == true) {
                                try {
                                    result.setValue(Decompress((byte[]) result.getValue()));
                                } catch (Exception e) {
                                }

                            }

                            if (isPerfStatsCollectorInitialized() && result.getValue() != null) {
                                _perfStatsCollector.incrementAvgItemSize(((byte[]) result.getValue()).length);
                            }
                            deflatMap.put(pair.getKey(), safeDeserialize(result.getValue(), _serializationContext, result.getFlag(), UserObjectType.CacheItem, cls));                        }
                        else {
                            deflatMap.put(pair.getKey(), safeDeserialize(result.getValue(), _serializationContext, result.getFlag(), UserObjectType.CacheItem, cls));
                        }
                    }
                }
            }
            if (_perfStatsCollector != null)
            {
                stats.EndSample();
                _perfStatsCollector.incrementMsecPerGetSample(stats.getCurrent());
                _perfStatsCollector.incrementByGetPerSecStats(keysArr.length);
            }
            return deflatMap;
//        } catch (OperationFailedException| GeneralFailureException| AggregateException| SecurityException| ConfigurationException|RuntimeException e) {
//            if (_exceptionEnabled) {
//                throw e;
//            }
//        }
        } catch (CacheException e) {
            if (_exceptionEnabled) {
                throw e;
            }
        }
        return null;
    }


    @Override

    public Map addBulk(Map items) throws GeneralFailureException, OperationFailedException, SecurityException, java.lang.IllegalArgumentException, ConfigurationException, AggregateException, OperationNotSupportedException, StreamException, LicensingException, CommandException, StreamNotFoundException, StreamAlreadyLockedException {
        String providerName = null;
        java.util.Map itemVersions = null;
        long[] sizes = new long[items.size()];

        EnumSet eventTypes = EnumSet.of(EventTypeInternal.None);
        tangible.RefObject tempRef_providerName = new tangible.RefObject(providerName);
        RefObject> tempRef_eventTypes = new RefObject(eventTypes);
        providerName = tempRef_providerName.argvalue;

        eventTypes = tempRef_eventTypes.argvalue;
        //Consider only first element from enumset. For now multiple events are not supported.
        EventTypeInternal eventTypeInternal = (EventTypeInternal) eventTypes.toArray()[0];

        tangible.RefObject tempRef_sizes = new tangible.RefObject(sizes);
        tangible.RefObject tempRef_itemVersions = new tangible.RefObject(itemVersions);

        return AddBulkInternal(items,  eventTypeInternal, providerName, true, null, (short) -1,
                (short) -1,  EventDataFilter.None, EventDataFilter.None, false);

    }

    public Map AddBulkInternal(Map items, EventTypeInternal eventType, String providerName, boolean allowQueryTags, String clientId, short updateCallbackId, short removeCallbackId,  EventDataFilter updateCallbackFilter, EventDataFilter removeCallabackFilter, boolean returnVersions)
            throws GeneralFailureException, OperationFailedException, AggregateException, SecurityException, ConfigurationException, StreamNotFoundException, OperationNotSupportedException, StreamException, CommandException, LicensingException, StreamAlreadyLockedException {
        if (_cacheImpl == null) {
            throw new OperationFailedException(ErrorMessages.getErrorMessage(ErrorCodes.CacheInit.CACHE_NOT_INIT));
        }

        if (items == null) {
            throw new java.lang.IllegalArgumentException("Value cannot be null."+System.lineSeparator()+"Parameter name: items");
        }
        if (items.isEmpty()) {
            throw new java.lang.IllegalArgumentException("Adding empty dictionary into cache.");
        }

        if (!tangible.DotNetToJavaStringHelper.isNullOrEmpty(providerName))
        {
            providerName.toLowerCase();
        }

        java.util.Map itemVersions = null ;
        long[] sizes = new long[items.size()];

        CacheItem[] clonedItems = new CacheItem[items.size()];
        String[] keys = new String[items.size()];

        String[] dataTypes = new String[items.size()];

        UsageStats stats = new UsageStats();
        stats.BeginSample();

        int count = 0;
        for (Iterator> it = items.entrySet().iterator(); it.hasNext(); ) {
            Map.Entry pair = it.next();
            String key = pair.getKey();
            CacheItem cacheitem = pair.getValue();
            if (cacheitem== null) {
                throw new java.lang.IllegalArgumentException("CacheItem cannot be null");
            }
            if(key==null)
                throw new IllegalArgumentException("Value cannot be null."+System.lineSeparator()+"Parameter name: key");


            Object tempVar = cacheitem.clone();
            CacheItem cloned = (CacheItem) ((tempVar instanceof CacheItem) ? tempVar : null);

            if (cloned == null) {
                throw new java.lang.IllegalArgumentException("items[" + count + "]");
            }

            if (cloned.getGroup() != null) {
                CacheItemWrapperInternal.setTypeName(cloned, getTypeName(cloned.getValue(Object.class)));
            }

            BitSet flagMap = new BitSet();

            long size = 0;
            if (sizes[count] > 0) {
                size = sizes[count];
            }
            java.util.HashMap queryInfo = null;
            if (cloned.getValue(Object.class) instanceof ClientCacheItem) {
                Object tempVar2 = cloned.getValue(Object.class);
                ClientCacheItem clientcacheItem = (ClientCacheItem) ((tempVar2 instanceof ClientCacheItem) ? tempVar2 : null);
                cloned.setValue(clientcacheItem.getValue());
                queryInfo = clientcacheItem.getQueryInfo();

                if (clientcacheItem.getFlags().IsAnyBitSet((byte) BitSetConstants.BinaryData)) {
                    flagMap.SetBit((byte) BitSetConstants.BinaryData);
                } else if (clientcacheItem.getFlags().IsAnyBitSet((byte) BitSetConstants.JsonData)) {
                    flagMap.SetBit((byte) BitSetConstants.JsonData);
                }
            }
            if (queryInfo == null) {
                queryInfo = new java.util.HashMap();
                if (allowQueryTags) {
                    queryInfo.put("query-info", getQueryInfo(cloned.getValue(Object.class)));


                }
            }

            CacheItemWrapperInternal.setQueryInfo(cloned, queryInfo);

            cloned.setValue(safeSerialize(cloned.getValue(Object.class), _serializationContext, flagMap, size, UserObjectType.CacheItem, false));
            sizes[count] = size;



            if (isPerfStatsCollectorInitialized()) {
                if (cloned.getValue(Object.class) != null && cloned.getValue(Object.class) instanceof byte[]) {
                    _perfStatsCollector.incrementAvgItemSize(((byte[]) cloned.getValue(Object.class)).length);
                }
            }

            if (_compressionEnabled) {
                tangible.RefObject tempRef_flagMap2 = new tangible.RefObject(flagMap);
                cloned.setValue(Compress((byte[]) cloned.getValue(Object.class), tempRef_flagMap2.getValue(), _compressionThresholdSize));
                flagMap = tempRef_flagMap2.argvalue;
            }


            CacheItemWrapperInternal.setFlagValue(cloned,flagMap);

            if (ConversionUtil.getAbsoluteExpiration(cloned.getExpiration()) != null && ConversionUtil.getAbsoluteExpiration(cloned.getExpiration()) != NoAbsoluteExpiration) {
                Date expirationTime = null;
                Date nowTme = null;
                try {
                    expirationTime = NCDateTime.getUTCDate(ConversionUtil.getAbsoluteExpiration(cloned.getExpiration()));
                    nowTme = NCDateTime.getUTCDate(new Date());
                } catch (ParseException e) {
                    e.printStackTrace();
                }
                Expiration tempVar3 = new Expiration(ExpirationType.Absolute, TimeSpan.subtract(expirationTime, nowTme));
                cloned.setExpiration(tempVar3);
            }
            clonedItems[count] = cloned;
            keys[count] = key;
            count++;
        }




        try {
            java.util.Map result = _cacheImpl.add(keys, clonedItems, providerName, sizes ,_cacheImpl.getEncryptionEnabled(), clientId, updateCallbackId, removeCallbackId ,updateCallbackFilter ,removeCallabackFilter,returnVersions,itemVersions ,ListenerType.PushBasedNotification);
            if (isPerfStatsCollectorInitialized()) {
                stats.EndSample();
                _perfStatsCollector.incrementMsecPerAddBulkSample(stats.getCurrent());
                _perfStatsCollector.incrementByAddPerSecStats(keys.length);
            }


            return result;
        } catch (RuntimeException e) {
            if (_exceptionEnabled) {
                throw e;
            }
        }
        return null;
    }

    @Override

    public Map insertBulk(Map items) throws CacheException {
        String providerName = null;
        java.util.Map itemVersions = null;
        long[] sizes = new long[items.size()];

        EnumSet eventTypes = EnumSet.of(EventTypeInternal.None);

       tangible.RefObject tempRef_providerName = new tangible.RefObject(providerName);
        RefObject> tempRef_eventTypes = new RefObject(eventTypes);

        providerName = tempRef_providerName.argvalue;
        eventTypes = tempRef_eventTypes.argvalue;
        //Consider only first element from enumset. For now multiple events are not supported.
        EventTypeInternal eventTypeInternal = (EventTypeInternal) eventTypes.toArray()[0];

        tangible.RefObject tempRef_sizes = new tangible.RefObject(sizes);
        tangible.RefObject tempRef_itemVersions = new tangible.RefObject(itemVersions);
        return InsertBulkInternal(items, eventTypeInternal, providerName, tempRef_sizes, true, "", (short) -1, (short) -1, EventDataFilter.None, EventDataFilter.None, false, tempRef_itemVersions);
    }

    public java.util.Map InsertBulkInternal(java.util.Map items,  EventTypeInternal eventType, String providerName, tangible.RefObject sizes, boolean allowQueryTags, String clientId, short updateCallbackId, short removeCallbackId, EventDataFilter updateCallbackFilter, EventDataFilter removeCallbackFilter, boolean returnVersions, tangible.RefObject itemVersions) throws CacheException {
        if (_cacheImpl == null) {
            throw new OperationFailedException(ErrorMessages.getErrorMessage(ErrorCodes.CacheInit.CACHE_NOT_INIT));
        }

        if (items == null) {
            throw new java.lang.IllegalArgumentException("Value cannot be null."+System.lineSeparator()+"Parameter name: items");
        }
        if (items.isEmpty()) {
            throw new java.lang.IllegalArgumentException("Adding empty dictionary into cache.");
        }
        itemVersions.argvalue = null;


        if (!tangible.DotNetToJavaStringHelper.isNullOrEmpty(providerName)) {
            providerName = providerName.toLowerCase();
        }

        CacheItem[] clonedItems = new CacheItem[items.size()];
        String[] keys = new String[items.size()];

        UsageStats stats = new UsageStats();
        stats.BeginSample();

        int count = 0;
        for (Iterator> it = items.entrySet().iterator(); it.hasNext(); ) {
            Map.Entry pair = it.next();
            CacheItem cacheItem = pair.getValue();
            String key = pair.getKey();

            if (cacheItem == null) {
                throw new java.lang.IllegalArgumentException("CacheItem cannot be null");
            }



            CacheItem cloned = (CacheItem)  cacheItem.clone();

            validateKeyValue(key, cloned.getValue(Object.class));

            BitSet flagMap = new BitSet();

            if (cloned.getGroup() != null) {
                CacheItemWrapperInternal.setTypeName(cloned, getTypeName(cloned.getValue(Object.class)));
            }

            long size = 0;
            if (sizes.argvalue[count] > 0) {
                size = sizes.argvalue[count];
            }

            HashMap queryInfo = null;

            if (cloned.getValue(Object.class) instanceof ClientCacheItem) {
                Object tempVar2 = cloned.getValue(Object.class);
                ClientCacheItem clientcacheItem = (ClientCacheItem) ((tempVar2 instanceof ClientCacheItem) ? tempVar2 : null);
                cloned.setValue(clientcacheItem.getValue());
                queryInfo = clientcacheItem.getQueryInfo();

                if (clientcacheItem.getFlags().IsAnyBitSet((byte) BitSetConstants.BinaryData)) {
                    flagMap.SetBit((byte) BitSetConstants.BinaryData);
                } else if (clientcacheItem.getFlags().IsAnyBitSet((byte) BitSetConstants.JsonData)) {
                    flagMap.SetBit((byte) BitSetConstants.JsonData);
                }
                CacheItemWrapperInternal.setTypeName(cloned,clientcacheItem.getGroupDataType());
            }

            if (queryInfo == null) {
                queryInfo = new HashMap();
                if (allowQueryTags) {
                    queryInfo.put("query-info", getQueryInfo(cloned.getValue(Object.class)));

                }
            }

            CacheItemWrapperInternal.setQueryInfo(cloned, queryInfo);

            cloned.setValue(safeSerialize(cloned.getValue(Object.class), _serializationContext, flagMap, size, UserObjectType.CacheItem, false));
            sizes.argvalue[count] = size;



            if (isPerfStatsCollectorInitialized()) {
                if (cloned.getValue(Object.class) != null) {
                    _perfStatsCollector.incrementAvgItemSize(((byte[]) cloned.getValue(Object.class)).length);
                }
            }

            if (_compressionEnabled == true) {
                RefObject tempRef_flagMap2 = new RefObject(flagMap);
                cloned.setValue(Compress((byte[]) cloned.getValue(Object.class), tempRef_flagMap2.getValue(), _compressionThresholdSize));
                flagMap = tempRef_flagMap2.argvalue;
            }



            CacheItemWrapperInternal.setFlagValue(cloned,flagMap);

            if (ConversionUtil.getAbsoluteExpiration(cloned.getExpiration()) != null && ConversionUtil.getAbsoluteExpiration(cloned.getExpiration()) != NoAbsoluteExpiration) {
                Date expirationTime = null;
                Date nowTme = null;
                try {
                    expirationTime = NCDateTime.getUTCDate(ConversionUtil.getAbsoluteExpiration(cloned.getExpiration()));
                    nowTme = NCDateTime.getUTCDate(new Date());

                } catch (ParseException e) {
                    e.printStackTrace();
                }
                Expiration tempVar3 = new Expiration(ExpirationType.Absolute, TimeSpan.subtract(expirationTime, nowTme));
                cloned.setExpiration(tempVar3);
            }

            clonedItems[count] = cloned;
            keys[count] = key;
            count++;
        }




        try {
            java.util.Map result = _cacheImpl.insert(keys, clonedItems, sizes.argvalue , _cacheImpl.getEncryptionEnabled() , clientId,
                    updateCallbackId,removeCallbackId,updateCallbackFilter,removeCallbackFilter,returnVersions,itemVersions.argvalue,ListenerType.PushBasedNotification);

            if (isPerfStatsCollectorInitialized()) {
                stats.EndSample();
                _perfStatsCollector.incrementMsecPerUpdBulkSample(stats.getCurrent());
                _perfStatsCollector.incrementByUpdPerSecStats(keys.length);
            }

            return result;
        } catch (CacheException e) {
            if (_exceptionEnabled) {
                throw e;
            }
            return null;
        }
    }

    @Override

    public void deleteBulk(Iterable keys) throws CacheException {
        if (_cacheImpl == null) {
            throw new OperationFailedException(ErrorMessages.getErrorMessage(ErrorCodes.CacheInit.CACHE_NOT_INIT));
        }
        if (keys == null) {
            throw new java.lang.IllegalArgumentException("Value cannot be null."+System.lineSeparator()+"Parameter name: keys");
        }
        String[] keysArr = removeDuplicateKeys(keys);

        if (keysArr.length == 0) {
            throw new java.lang.IllegalArgumentException("There is no key present in keys array");
        }

        String providerName = null;
        BitSet flagMap = new BitSet();
        short dsItemsRemovedCallbackId = -1;
        UsageStats stats = new UsageStats();
        EnumSet eventTypes = EnumSet.of(EventTypeInternal.None);

        RefObject tempRef_providerName = new RefObject(providerName);
        RefObject> tempRef_eventTypes = new RefObject(eventTypes);
        providerName = tempRef_providerName.argvalue;
        eventTypes = tempRef_eventTypes.argvalue;
        //Consider only first element from enumset. For now multiple events are not supported.
        EventTypeInternal eventTypeInternal = (EventTypeInternal) eventTypes.toArray()[0];

        try {

            stats.BeginSample();

            if (!tangible.DotNetToJavaStringHelper.isNullOrEmpty(providerName))
            {
                providerName.toLowerCase();
            }

            _cacheImpl.delete(keysArr, flagMap, providerName, dsItemsRemovedCallbackId);

            if (isPerfStatsCollectorInitialized()) {
                stats.EndSample();
                _perfStatsCollector.incrementMsecPerDelBulkSample(stats.getCurrent());
                _perfStatsCollector.incrementByDelPerSecStats(keysArr.length);
            }
        } catch (Exception e) {
            if (_exceptionEnabled) {
                throw new CacheException(e);
            }
        }
    }

    @Override

    public  Map removeBulk(Iterable keys, Class cls) throws CacheException, IllegalArgumentException, ClassCastException {
        Map removedItems = new HashMap<>();
        if (_cacheImpl == null) {
            throw new OperationFailedException(ErrorMessages.getErrorMessage(ErrorCodes.CacheInit.CACHE_NOT_INIT));
        }
        if (keys == null) {
            throw new java.lang.IllegalArgumentException("keys");
        }
        String[] keysArr = removeDuplicateKeys(keys);

        if (keysArr.length == 0) {
            throw new java.lang.IllegalArgumentException("There is no key present in keys array");
        }

        String providerName = null;
        BitSet flagMap = new BitSet();
        short dsItemsRemovedCallbackId = -1;
        UsageStats stats = new UsageStats();
        EnumSet eventTypes = EnumSet.of(EventTypeInternal.None);
        RefObject tempRef_providerName = new RefObject(providerName);
        RefObject> tempRef_eventTypes = new RefObject(eventTypes);
        providerName = tempRef_providerName.argvalue;
        eventTypes = tempRef_eventTypes.argvalue;
        //Consider only first element from enumset. For now multiple events are not supported.
        EventTypeInternal eventTypeInternal = (EventTypeInternal) eventTypes.toArray()[0];

        try {

            stats.BeginSample();

            if (!tangible.DotNetToJavaStringHelper.isNullOrEmpty(providerName))
            {
                providerName.toLowerCase();
            }

            Map map = _cacheImpl.remove(keysArr, flagMap, providerName, dsItemsRemovedCallbackId);
            if (map != null)
            {
                java.util.Iterator ie = map.keySet().iterator();
                while (ie.hasNext())
                {
                    String key = ie.next().toString();
                    CompressedValueEntry result = (CompressedValueEntry)((map.get(key) instanceof CompressedValueEntry) ? map.get(key) : null);
                    if (result != null)
                    {
                        if (result.getType() != EntryType.CacheItem)
                        {
                            map.put(key , null);
                            continue;
                        }

                        if (result.getFlag().IsBitSet((byte) _compressed))
                        {
                            result.setValue(Decompress((byte[])result.getValue()));
                        }

                        if (_perfStatsCollector != null && result.getValue() != null && result.getValue() instanceof byte[])
                        {
                            _perfStatsCollector.incrementAvgItemSize(((byte[])result.getValue()).length);
                        }



                        removedItems.put(key,safeDeserialize(result.getValue(), _serializationContext, result.getFlag(), UserObjectType.CacheItem, cls));
                    }
                }
            }
            if (isPerfStatsCollectorInitialized()) {
                stats.EndSample();
                _perfStatsCollector.incrementMsecPerDelBulkSample(stats.getCurrent());
                _perfStatsCollector.incrementByDelPerSecStats(keysArr.length);
            }
        } catch (Exception e) {
            if (_exceptionEnabled) {
                throw new CacheException(e);
            }
        }
        return removedItems;
    }


    ///endregion

    ///region Data Type

    private EventTypeInternal convertToEventType(EventType type) {
        EventTypeInternal eventType = EventTypeInternal.None;
        switch (type) {
            case ItemAdded:
                eventType = EventTypeInternal.ItemAdded;
                break;
            case ItemRemoved:
                eventType = EventTypeInternal.ItemRemoved;
                break;
            case ItemUpdated:
                eventType = EventTypeInternal.ItemUpdated;
                break;

        }
        return eventType;
    }

    ///region CallbackIds New
    public short getCallbackId(CacheItemRemovedListener removedCallback, ListenerType listenerType) throws CacheException {
        if (removedCallback == null) {
            return -1;
        }

        return _eventManager.registerSelectiveCallback(removedCallback, listenerType);
    }

    public short getCallbackId(CacheItemRemovedListener removedCallback) throws CacheException {
        return getCallbackId(removedCallback, ListenerType.PushBasedNotification);
    }

    public short getCallbackId(CacheItemUpdatedListener updateCallback) throws CacheException {
        return getCallbackId(updateCallback, ListenerType.PushBasedNotification);
    }

    public short getCallbackId(CacheItemUpdatedListener updateCallback, ListenerType listenerType) throws CacheException {
        if (updateCallback == null) {
            return -1;
        }

        return _eventManager.registerSelectiveCallback(updateCallback, listenerType);
    }

    ///endregion

    ///endregion data structures ends


    //region Cache operations (Non CRUD)

    ///region Count
    @Override
    public long getCount() throws CacheException {
        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));
        }
        return _cacheImpl.getCount();

    }

    FutureTask clearAsync() {
        FutureTask task = new FutureTask(() -> {
            clearInternal();
            return null;
        });
        return (FutureTask) ThreadPool.getInstance().submitTask(task);
    }

    FutureTask clearClientCacheAsync() {
        return null;
    }

    ///endregion

    ///#region Clear Operations

    @Override
    public void clear() throws CacheException {
        clearInternal();
    }

    private void clearInternal() throws CacheException {
        if (_cacheImpl == null) {
            return;
        }
        try {
            String providerName = "";
            BitSet flagMap = new BitSet();
            short dsClearedCallbackId = -1;

            _cacheImpl.clear(flagMap, dsClearedCallbackId, providerName);

            if (_callbackIDsMap != null) {
                _callbackIDsMap.removeAllResources();
            }

            if (_callbacksMap != null) {
                _callbacksMap.removeAllResources();
            }
        } catch (Exception e) {
            if (getExceptionEnabled()) {
                throw new CacheException(e.getMessage(), e.getCause());
            }
        }
    }

    ///endregion

    ///#region Contains Operations

    @Override
    public boolean contains(String key) throws CacheException {
        validateKey(key);
        if (!(key.getClass() instanceof Serializable)) {
            throw new java.lang.IllegalArgumentException("key is not serializable");
        }
        if (_cacheImpl == null) {
            return false;
        }
        try {
            return _cacheImpl.contains(key);
        } catch (Exception e) {
            if (getExceptionEnabled()) {
                throw new CacheException(e.getMessage(), e.getCause());
            }
        }
        return false;
    }

    @Override
    public Map containsBulk(Iterable keys) throws CacheException
    {
        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));
        }
        if (keys == null) {
            throw new java.lang.IllegalArgumentException("keys is null");
        }

        String[] keysArr = removeDuplicateKeys(keys);
        if (keysArr.length == 0) {
            throw new java.lang.IllegalArgumentException("There is no key present in keys array");
        }

        try {
            return _cacheImpl.containsBulk(keysArr);
        } catch (Exception e) {
            if (getExceptionEnabled()) {
                throw new CacheException(e.getMessage(), e.getCause());
            }
        }

        return null;
    }

    ///endregion

    //region Locking Operations
    @Override
    public void unlock(String key) throws CacheException {
        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));
        }
        validateKey(key);
        try {
            _cacheImpl.unlock(key);
        } catch (Exception e) {
            if (getExceptionEnabled()) {
                throw new CacheException(e.getMessage(), e.getCause());
            }
        }
    }

    @Override
    public void unlock(String key, LockHandle lockHandle) throws CacheException {
        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));
        }
        validateKey(key);
        if(lockHandle == null){
            throw new java.lang.IllegalArgumentException("Value cannot be null.\r\nParameter name: LockHandle");
        }
        String lockId = lockHandle.getLockId();

        try {
            _cacheImpl.unlock(key, lockId);
        } catch (Exception e) {
            if (getExceptionEnabled()) {
                throw new CacheException(e.getMessage(), e.getCause());
            }
        }
    }

    @Override
    public boolean lock(String key, TimeSpan lockTimeout, LockHandle lockHandle)
            throws CacheException {
        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));
        }
        validateKey(key);

        if(lockHandle == null){
            throw new java.lang.IllegalArgumentException("Value cannot be null.\r\nParameter name: LockHandle");
        }
        boolean lockAcquired = false;
        try {
            lockAcquired = _cacheImpl.lock(key, lockTimeout, lockHandle);
            if(lockHandle != null && !lockHandle.getLockDate().equals(NCDateTime.MinValue))
                lockHandle.setLockDate(new NCDateTime(lockHandle.getLockDate()).getLocalizedDate());
        } catch (Exception e) {
            if (getExceptionEnabled()) {
                throw new CacheException(e.getMessage(), e.getCause());
            }
        }
        return lockAcquired;
    }


    boolean isLocked(String key, LockHandle lockHandle)
            throws CacheException {
        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));
        }
        validateKey(key);

        if(lockHandle == null){
            throw new java.lang.IllegalArgumentException("Value cannot be null.\r\nParameter name: LockHandle");
        }
        try {
            boolean result =  _cacheImpl.isLocked(key, lockHandle);
            if(lockHandle != null && !lockHandle.getLockDate().equals(NCDateTime.MinValue))
                lockHandle.setLockDate(new NCDateTime(lockHandle.getLockDate()).getLocalizedDate());
            return result;
        } catch (Exception e) {
            if (getExceptionEnabled()) {
                throw new CacheException(e.getMessage(), e.getCause());
            }
        }
        return false;
    }

    ///#region enumeration

    @Override
    public boolean hasMoreElements() {
        if(enumerator == null)
            enumerator = new WebCacheEnumerator(_serializationContext, null, null, getCacheInstanceInternal(),Object.class);

        boolean hasMore= enumerator.hasMoreElements();
        if(!hasMore)
            enumerator=null;
        return hasMore;
    }

    @Override
    public Object nextElement() {
        if(enumerator == null)
            enumerator = new WebCacheEnumerator(_serializationContext, null, null, getCacheInstanceInternal(),Object.class);
        return enumerator.nextElement();
    }

    @Override
    public Iterator asJsonIterator()
    {
        return (Iterator) new WebCacheEnumerator(_serializationContext,null,null,getCacheInstanceInternal(), JsonValueBase.class).asIterator();
    }

    ///#endregion



    //endregion

    //region Enumerator Methods

    ArrayList getNextChunk(ArrayList pointer) {
        ArrayList chunks = null;
        try {
            chunks = _cacheImpl.getNextChunk(pointer);
        } catch (Exception ex) {
            //this is a empty call just to dispose the enumeration pointers for this particular enumerator
            //on all the nodes.
            for (int i = 0; i < pointer.size(); i++) {
                pointer.get(i).setDisposable(true);
            }
            try {
                _cacheImpl.getNextChunk(pointer);
            } catch (Exception exc) {
                if (getExceptionEnabled()) {
                    throw new ConcurrentModificationException(ex.getMessage());
                }
            }
        }

        return chunks;
    }

    ///endregion

    //region Stream Operations

//    @Override
//    public CacheStream getCacheStream(String key, CacheStreamAttributes cacheStreamAttributes) throws CacheException {
//        Date absTime = NoAbsoluteExpiration;
//        TimeSpan defaultSldTime = NoSlidingExpiration;
//        if (cacheStreamAttributes == null) {
//            throw new java.lang.IllegalArgumentException("CacheStreamAttributes");
//        }
//        if (cacheStreamAttributes.getExpiration() != null) {
//            absTime = ConversionUtil.getExpirationAbsoluteInternal(cacheStreamAttributes.getExpiration());
//            defaultSldTime = ConversionUtil.getExpirationSlidingInternal(cacheStreamAttributes.getExpiration());
//        }
//        return getCacheStreamInternal(key, cacheStreamAttributes.getStreamMode(), cacheStreamAttributes.getGroup(), null, cacheStreamAttributes.getCacheDependency(), absTime, defaultSldTime, cacheStreamAttributes.getCacheItemPriority());
//    }
//
//
//    CacheStream getCacheStreamInternal(String key, StreamMode streamMode, String group, String subGroup, CacheDependency dependency, Date absoluteExpiration, TimeSpan slidingExpiration, CacheItemPriority priority) throws CacheException
//    {
//        validateKey(key);
//
//        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));
//            }
//
//        CacheStreamImpl stream = new CacheStreamImpl();
//        stream.setCacheStream(createStream(key,streamMode,group,dependency,absoluteExpiration,slidingExpiration,priority));
//
//        return stream;
//    }
//
//    private CacheStream createStream(String key, StreamMode streamMode, String group, CacheDependency dependency, Date absoluteExpiration, TimeSpan slidingExpiration, CacheItemPriority priority) throws CacheException {
//        String _lockHandle;
//
//        CacheStream cacheStream=null;
//
//        if (streamMode == StreamMode.Read) {
//            _lockHandle = openStream(key, StreamMode.Read, group, absoluteExpiration, slidingExpiration, dependency, priority);
//
//            if (_lockHandle == null)
//                throw new StreamException("An error occured while opening stream");
//
//            long length = getStreamLength(key, _lockHandle);
//            cacheStream = new InputCacheStream(key, streamMode, this, _lockHandle, length);
//
//        } else if (streamMode == StreamMode.ReadWithoutLock) {
//            String lockHandle = openStream(key, StreamMode.ReadWithoutLock, group, absoluteExpiration, slidingExpiration, dependency, priority);
//
//            if (lockHandle != null) {
//                _lockHandle = "";
//                cacheStream = new InputCacheStream(key, streamMode, this, _lockHandle, 0);
//
//            } else {
//                throw new StreamNotFoundException();
//            }
//        } else {
//            _lockHandle = openStream(key, StreamMode.Write, group, absoluteExpiration, slidingExpiration, dependency, priority);
//
//            if (_lockHandle == null)
//                throw new StreamException("An error occured while opening stream");
//
//            long length = getStreamLength(key, _lockHandle);
//            cacheStream = new OutputCacheStream(key, streamMode, this, _lockHandle, length);
//        }
//
//        return cacheStream;
//    }
//
//    String openStream(String key, StreamMode mode, String group, Date absExpiration, TimeSpan slidingExpiration, CacheDependency dependency, CacheItemPriority priority) throws CacheException {
//        try {
//            return _cacheImpl.openStream(key, mode, group, absExpiration, slidingExpiration, dependency, priority);
//        } catch (Exception e) {
//            if (getExceptionEnabled()) {
//                throw new CacheException(e.getMessage(), e.getCause());
//            }
//        }
//        return null;
//    }
//
//    void closeStream(String key, String lockHandle) throws CacheException {
//        try {
//            _cacheImpl.closeStream(key, lockHandle);
//        } catch (Exception e) {
//            if (getExceptionEnabled()) {
//                throw new CacheException(e.getMessage(), e.getCause());
//            }
//        }
//    }
//
//    int readFromStream(byte[] buffer, String key, String lockHandle, int offset, int streamOffset, int length) throws CacheException {
//        try {
//            tangible.RefObject temp_buffer = new tangible.RefObject(buffer);
//            return _cacheImpl.readFromStream(temp_buffer, key, lockHandle, offset, streamOffset, length);
//        } catch (Exception e) {
//            if (getExceptionEnabled()) {
//                throw new CacheException(e.getMessage(), e.getCause());
//            }
//        }
//        return 0;
//    }
//
//    void writeToStream(String key, String lockHandle, byte[] buffer, int srcOffset, int dstOffset, int length) throws CacheException {
//        try {
//            _cacheImpl.writeToStream(key, lockHandle, buffer, srcOffset, dstOffset, length);
//        } catch (Exception e) {
//            if (getExceptionEnabled()) {
//                throw new CacheException(e.getMessage(), e.getCause());
//            }
//        }
//    }
//
//    long getStreamLength(String key, String lockHandle) throws CacheException {
//        try {
//            return _cacheImpl.getStreamLength(key, lockHandle);
//        } catch (Exception e) {
//            if (getExceptionEnabled()) {
//                throw new CacheException(e.getMessage(), e.getCause());
//            }
//        }
//        return 0;
//    }
//    ///endregion

    //region ToString()
    @Override
    public String toString() {
        return _cacheId;
    }

    ///endregion


    ///endregion

    ///endregion

    //region Search Service Methods





    int executeNonQuery(String query, Map values) throws CacheException {
        if (_cacheImpl == null) {
            throw new OperationFailedException(ErrorCodes.CacheInit.CACHE_NOT_INIT, ErrorMessages.getErrorMessage(ErrorCodes.CacheInit.CACHE_NOT_INIT));
        }
        int effectedKeys = 0;
        try {
            effectedKeys = _cacheImpl.executeNonQuery(query, (HashMap) values);
        } catch (Exception e) {
            if (getExceptionEnabled()) {
                throw new CacheException(e.getMessage(), e.getCause());
            }
        }
        return effectedKeys;
    }



    CacheReader executeReader(String query, Map values, boolean getData, int chunkSize) throws CacheException {
        if (_cacheImpl == null) {
            throw new OperationFailedException(ErrorCodes.CacheInit.CACHE_NOT_INIT, ErrorMessages.getErrorMessage(ErrorCodes.CacheInit.CACHE_NOT_INIT));
        }

        CacheReader reader = null;
        try {
            boolean isClientReader = false;
            String value = System.getProperty("UseClientSideReader");
            if (!isNullOrEmpty(value)) {
                isClientReader = Boolean.parseBoolean(value);
            }
            if (isClientReader) {
                isClientReader = !(query.toLowerCase().contains("group by") || query.toLowerCase().contains("order by"));
            }

            if (isClientReader) {
                ClientRecordSetEnumerator clientRecordSet = null;
                QueryResultSet resultSet = _cacheImpl.search(query, (HashMap) values);

                switch (resultSet.getType()) {
                    case AggregateFunction:
                        List resultAggregate = new ArrayList();
                        if (resultSet.getAggregateFunctionResult().getValue() != null) {
                            if (resultSet.getAggregateFunctionResult().getKey().toString().toUpperCase().equals(AggregateFunctionType.AVG.toString().toUpperCase())) {
                                resultSet.setAggregateFunctionResult(new DictionaryEntry("AVG", ((AverageResult) resultSet.getAggregateFunctionResult().getValue()).getAverage()));
                            }
                            resultAggregate.add(resultSet.getAggregateFunctionResult().getValue());

                        }
                        clientRecordSet = new ClientRecordSetEnumeratorImpl(resultAggregate, this, false);
                        break;
                    case SearchKeys:
                        clientRecordSet = new ClientRecordSetEnumeratorImpl(resultSet.getSearchKeysResult(), this, getData);
                        break;
                }
                reader = new ClientDataReader(this, clientRecordSet);
            } else {
                reader = new CacheDataReader(this, _cacheImpl.executeReader(query, values, getData, chunkSize));
            }
        } catch (Exception e) {
                throw new CacheException(e.getMessage(), e.getCause());
        }
        return reader;
    }

    protected CacheEventDescriptor addCacheDataModificationListener(String key, CacheDataModificationListener listener, EventType eventType, EventDataFilter dataFilter, boolean notifyOnItemExpiration) throws CacheException {
        if (_cacheImpl == null) {
            throw new OperationFailedException("Cache is not initialized");
        }
        CacheEventDescriptor discriptor = null;

        try {
            if (key != null) {
                short[] callbackRefs = _eventManager.registerSelectiveEvent(listener, EnumSet.of(eventType), dataFilter);
                _cacheImpl.registerKeyNotificationListener(key, callbackRefs[0], callbackRefs[1], dataFilter, notifyOnItemExpiration);
            } else {
                discriptor = _eventManager.registerGeneralEvents(listener,EnumSet.of(eventType), dataFilter);
            }
        } catch (Exception e) {
            if (getExceptionEnabled()) {
                throw new CacheException(e.getMessage(), e.getCause());
            }
        }
        return discriptor;
    }

    public void addCacheDataModificationListener(String key, CacheDataModificationListener listener, EventType eventType) throws CacheException {

        addCacheDataModificationListener(key, listener, eventType, EventDataFilter.None);

    }

    public void addCacheDataModificationListener(String key, CacheDataModificationListener listener, EventType eventType, EventDataFilter dataFilter) throws CacheException {
        validateKey(key);

        if (listener == null) {
            throw new java.lang.IllegalArgumentException("selectiveCacheDataNotificationCallback");
        }

        if (eventType == null) {
            throw new IllegalArgumentException("Value cannot be null."+System.lineSeparator()+"Parameter name: eventType");
        }
        addCacheDataModificationListener(key, listener, eventType, dataFilter, true);
    }



    //endregion

    //region Messaging Service Metods

    public void addCacheDataNotificationListener(String[] keys, CacheDataModificationListener listener, EnumSet eventTypes, EventDataFilter datafilter, boolean notifyOnItemExpiration) throws CacheException {
        addCacheDataNotificationListener(keys, listener, eventTypes, datafilter, notifyOnItemExpiration, ListenerType.PushBasedNotification);
    }

    public void addCacheDataNotificationListener(String[] keys, CacheDataModificationListener listener, EnumSet eventTypes, EventDataFilter datafilter, boolean notifyOnItemExpiration, ListenerType listenerType) throws CacheException {

        if (_cacheImpl == null) {
            throw new OperationFailedException(ErrorCodes.CacheInit.CACHE_NOT_INIT, ErrorMessages.getErrorMessage(ErrorCodes.CacheInit.CACHE_NOT_INIT));
        }

        try {
            if (keys != null) {
                    short[] callbackRefs = _eventManager.registerSelectiveEvent(listener, eventTypes, datafilter, listenerType);
                    _cacheImpl.registerKeyNotificationListener(keys, callbackRefs[0], callbackRefs[1], datafilter, notifyOnItemExpiration, listenerType);
            }
        } catch (RuntimeException ex) {
            if (getExceptionEnabled()) {
                throw new CacheException(ex.getMessage(), ex.getCause());
            }
        }
    }

    public void removeCacheNotification(CacheEventDescriptor discriptor) throws Exception {
        if (_cacheImpl == null) {
            throw new OperationFailedException(ErrorCodes.CacheInit.CACHE_NOT_INIT, ErrorMessages.getErrorMessage(ErrorCodes.CacheInit.CACHE_NOT_INIT));
        }
        if (discriptor == null) {
            throw new java.lang.IllegalArgumentException("Value cannot be null."+System.lineSeparator()+"Parameter name: discriptor");
        }

        if (!discriptor.getIsRegistered()) {
            return;
        }

        _eventManager.unRegisterDiscriptor(discriptor);
    }

    public void removeCacheNotification(String key, CacheDataModificationListener callback, EnumSet eventTypes) throws CacheException {
        if (_cacheImpl == null) {
            throw new OperationFailedException(ErrorCodes.CacheInit.CACHE_NOT_INIT, ErrorMessages.getErrorMessage(ErrorCodes.CacheInit.CACHE_NOT_INIT));
        }

        try {
            short[] value = _eventManager.unRegisterSelectiveNotification(callback, eventTypes);

            short update = value[0];
            short remove = value[1];

            _cacheImpl.unRegisterKeyNotificationListener(key, update, remove);
        } catch (Exception e) {
            if (getExceptionEnabled()) {
                throw new CacheException(e.getMessage(), e.getCause());
            }
        }
    }

    public void removeCacheNotification(String[] key, CacheDataModificationListener listener, EnumSet eventTypes) throws CacheException {
        if (_cacheImpl == null) {
            throw new OperationFailedException(ErrorCodes.CacheInit.CACHE_NOT_INIT, ErrorMessages.getErrorMessage(ErrorCodes.CacheInit.CACHE_NOT_INIT));
        }

        try {
            short[] value = _eventManager.unRegisterSelectiveNotification(listener, eventTypes);

            short update = value[0];
            short remove = value[1];

            _cacheImpl.unRegisterKeyNotificationListener(key, update, remove);
        } catch (Exception e) {
            if (getExceptionEnabled()) {
                throw new CacheException(e.getMessage(), e.getCause());
            }
        }
    }

    public void addCacheNotification(String key, CacheDataModificationListener cacheDataModificationListener, EnumSet eventTypes, EventDataFilter datafilter, ListenerType listenerType) throws CacheException {
        validateKey(key);



        if (cacheDataModificationListener == null) {
            throw new java.lang.IllegalArgumentException("Value cannot be null."+System.lineSeparator()+"Parameter name: selectiveCacheDataNotificationlistener");
        }

        registerCacheNotificationInternal(key, cacheDataModificationListener, eventTypes, datafilter, true, listenerType);
    }

    public void removeGeneralCacheNotification(EventTypeInternal eventType) throws CacheException {
        if (_cacheImpl != null) {
            _cacheImpl.unRegisterGeneralNotificationListener(eventType, (short) -1);
        }
    }

    public void addCacheNotificationDataFilter(EventTypeInternal eventType, EventDataFilter datafilter, short eventSequenceId) throws CacheException {
        if (_cacheImpl != null) {
            _cacheImpl.registerGeneralNotification(eventType, datafilter, eventSequenceId);
        }
    }

    public void registerSurrogateKeyNotificationCallback(String[] keys, short update, short remove, String clientId, ListenerType listenerType) throws CacheException {
        if (_cacheImpl == null) {
            throw new OperationFailedException(ErrorCodes.CacheInit.CACHE_NOT_INIT, ErrorMessages.getErrorMessage(ErrorCodes.CacheInit.CACHE_NOT_INIT));
        }

        try {
            if (keys != null) {
                _cacheImpl.registerKeyNotificationListener(keys, update, remove, clientId, listenerType);
            }
        } catch (Exception e) {
            if (getExceptionEnabled()) {
                throw new CacheException(e.getMessage(), e.getCause());
            }
        }
    }

    public void raiseCustomEvent(Object notifId, Object data) throws CacheException {
        BitSet flag = null;
        long size = 0;

        validateKeyValue((String) notifId, data);

        try {
            Object serializeNotifId = toByteBuffer(notifId, _serializationContext);
            Object serializeData = toByteBuffer(data, _serializationContext);

            _cacheImpl.raiseCustomEvent(serializeNotifId, serializeData);
        } catch (Exception e) {
            if (getExceptionEnabled()) {
                throw new CacheException(e.getMessage(), e.getCause());
            }
        }
    }

    public void registerKeyNotificationCallback(String key, CacheItemUpdatedListener updateCallback, CacheItemRemovedListener removeCallback, boolean notifyOnItemExpiration, ListenerType listenerType) throws CacheException {
        if (_cacheImpl == null) {
            throw new OperationFailedException(ErrorCodes.CacheInit.CACHE_NOT_INIT, ErrorMessages.getErrorMessage(ErrorCodes.CacheInit.CACHE_NOT_INIT));
        }

        validateKey(key);

        if (updateCallback == null && removeCallback == null) {
            throw new java.lang.IllegalArgumentException();
        }

        try {
            short updateCallbackid = -1;
            short removeCallbackid = -1;

            if (updateCallback != null) {
                updateCallbackid = getCallbackId(updateCallback, listenerType);
            }
            if (removeCallback != null) {
                removeCallbackid = getCallbackId(removeCallback, listenerType);
            }

            _cacheImpl.registerKeyNotificationListener(key, updateCallbackid, removeCallbackid, EventDataFilter.None, notifyOnItemExpiration, listenerType);

        } catch (Exception e) {
            if (getExceptionEnabled()) {
                throw new CacheException(e.getMessage(), e.getCause());
            }
        }
    }

    public short registerUpdateSorrogateCallback(CacheItemUpdatedListener onUpdateCallback, CacheDataModificationListener cacheItemUpdatedCallback, EventDataFilter eventDataFilter) {
        short updateCallbackId = -1;
        try {
            if (cacheItemUpdatedCallback != null) {
                short[] callabackIds = _eventManager.registerSelectiveEvent(cacheItemUpdatedCallback, EnumSet.of(EventType.ItemUpdated), eventDataFilter);
                updateCallbackId = callabackIds[0];
            } else if (onUpdateCallback != null) {
                updateCallbackId = getCallbackId(onUpdateCallback);
            }
        } catch (Exception e) {
        }
        return updateCallbackId;
    }

    public short registerRemoveSorrogateCallback(CacheItemRemovedListener onRemovedCallback, CacheDataModificationListener cacheItemRemovedCallback, EventDataFilter eventDataFilter) {
        short removeCallbackId = -1;
        try {
            if (cacheItemRemovedCallback != null) {
                short[] callabackIds = _eventManager.registerSelectiveEvent(cacheItemRemovedCallback, EnumSet.of(EventType.ItemRemoved), eventDataFilter);
                removeCallbackId = callabackIds[1];
            } else if (onRemovedCallback != null) {
                removeCallbackId = getCallbackId(onRemovedCallback);
            }
        } catch (Exception e) {
        }
        return removeCallbackId;
    }

    public void registerKeyNotificationCallback(String key, CacheItemUpdatedListener updateCallback, CacheItemRemovedListener removeCallback, boolean notifyOnItemExpiration) throws CacheException {
        if (_cacheImpl == null) {
            throw new OperationFailedException(ErrorCodes.CacheInit.CACHE_NOT_INIT, ErrorMessages.getErrorMessage(ErrorCodes.CacheInit.CACHE_NOT_INIT));
        }

        validateKey(key);

        if (updateCallback == null && removeCallback == null) {
            throw new java.lang.IllegalArgumentException();
        }
        try {
            short updateCallbackid = -1;
            short removeCallbackid = -1;

            if (updateCallback != null) {
                updateCallbackid = getCallbackId(updateCallback);
            }
            if (removeCallback != null) {
                removeCallbackid = getCallbackId(removeCallback);
            }

            _cacheImpl.registerKeyNotificationListener(key, updateCallbackid, removeCallbackid, notifyOnItemExpiration);

        } catch (Exception e) {
            if (getExceptionEnabled()) {
                throw new CacheException(e.getMessage(), e.getCause());
            }
        }
    }

    protected void registerPollingNotification(PollNotificationListener listener, EventTypeInternal eventType) {
        if (listener != null) {
            short callbackId = getEventManager().registerPollingEvent(listener, eventType);
        }
    }

    protected void touch(ArrayList keys) throws OperationFailedException, StreamNotFoundException, StreamException, SecurityException, CommandException, OperationNotSupportedException, AggregateException, StreamAlreadyLockedException, ConfigurationException, LicensingException, GeneralFailureException {
        if (getCacheImpl() == null)
        {
            throw new OperationFailedException(ErrorCodes.CacheInit.CACHE_NOT_INIT, ErrorMessages.getErrorMessage(ErrorCodes.CacheInit.CACHE_NOT_INIT));
        }
        if (keys == null)
        {
            throw new IllegalArgumentException("Value cannot be null."+System.lineSeparator()+"Parameter name: keys");
        }

        java.util.HashMap newKeys = new java.util.HashMap<>();

        for (int i = 0; i < keys.size(); i++)
        {
            if (!tangible.DotNetToJavaStringHelper.isNullOrEmpty(keys.get(i)))
            {
                newKeys.put(keys.get(i), null);
            }
        }

        try
        {
            getCacheImpl().touch(keys);
        }
        catch (CacheException e)
        {
            if (_exceptionEnabled)
            {
                throw e;
            }
        }
        finally
        {
        }
    }
    class AddAsyncCallable implements Callable {

        String _key;
        CacheItem _item;
        private CacheItemVersion waitTime;

        AddAsyncCallable(String key, CacheItem item) {
            _key = key;
            _item = item;

        }

        @Override
        public CacheItemVersion call() throws CacheException, java.lang.IllegalArgumentException {
            return AddInternal(_key, _item);
        }

    }

    class InsertAsyncCallable implements Callable {

        String _key;
        CacheItem _item;

        private CacheItemVersion waitTime;

        InsertAsyncCallable(String key, CacheItem item,
                            LockHandle lockhandle, boolean releaseLock) {
            _key = key;
            _item = item;

        }

        @Override
        public CacheItemVersion call() throws CacheException, java.lang.IllegalArgumentException {
            return InsertInternal(_key, _item, null, false);
        }

    }

    class RemoveAsyncCallable implements Callable {
        String _key;
        Class _class;
        RemoveAsyncCallable(String key, Class cls) {
            _key = key;
            _class=cls;
        }

        @Override
        public T call() throws CacheException {
            return (T) remove(_key,_class);
        }

    }

    class DeleteAsyncCallable implements Callable {
        String _key;

        DeleteAsyncCallable(String key)
        {
            _key = key;

    }

        @Override
        public Void call() throws CacheException {
            delete(_key);
            return null;
        }
    }

    class GeneralDataNotificationWrapper {
        private Cache _parentCache;

        public GeneralDataNotificationWrapper(Cache parentCache) {
            _parentCache = parentCache;
        }
    }

    //endregion
}