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

com.hazelcast.client.proxy.NearCachedClientMapProxy Maven / Gradle / Ivy

There is a newer version: 3.6.8
Show newest version
/*
 * Copyright (c) 2008-2015, Hazelcast, Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hazelcast.client.proxy;

import com.hazelcast.cache.impl.nearcache.NearCache;
import com.hazelcast.client.config.ClientConfig;
import com.hazelcast.client.impl.client.BaseClientRemoveListenerRequest;
import com.hazelcast.client.map.impl.nearcache.ClientHeapNearCache;
import com.hazelcast.client.spi.EventHandler;
import com.hazelcast.config.NearCacheConfig;
import com.hazelcast.core.ExecutionCallback;
import com.hazelcast.core.ICompletableFuture;
import com.hazelcast.logging.Logger;
import com.hazelcast.map.EntryProcessor;
import com.hazelcast.map.impl.MapEntries;
import com.hazelcast.map.impl.client.MapAddNearCacheEntryListenerRequest;
import com.hazelcast.map.impl.client.MapRemoveEntryListenerRequest;
import com.hazelcast.map.impl.nearcache.BatchNearCacheInvalidation;
import com.hazelcast.map.impl.nearcache.CleaningNearCacheInvalidation;
import com.hazelcast.map.impl.nearcache.SingleNearCacheInvalidation;
import com.hazelcast.monitor.LocalMapStats;
import com.hazelcast.monitor.NearCacheStats;
import com.hazelcast.monitor.impl.LocalMapStatsImpl;
import com.hazelcast.monitor.impl.NearCacheStatsImpl;
import com.hazelcast.nio.serialization.Data;
import com.hazelcast.util.executor.CompletedFuture;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import static com.hazelcast.cache.impl.nearcache.NearCache.NULL_OBJECT;
import static com.hazelcast.util.MapUtil.createHashMap;
import static java.util.Collections.emptyMap;

/**
 * A Client-side {@code IMap} implementation which is fronted by a near-cache.
 *
 * @param  the key type for this {@code IMap} proxy.
 * @param  the value type for this {@code IMap} proxy.
 */
public class NearCachedClientMapProxy extends ClientMapProxy {

    protected NearCache nearCache;
    protected volatile String invalidationListenerId;

    public NearCachedClientMapProxy(String serviceName, String name) {
        super(serviceName, name);
    }

    @Override
    protected void onInitialize() {
        super.onInitialize();

        init();
    }

    protected void init() {
        ClientConfig clientConfig = getContext().getClientConfig();
        NearCacheConfig nearCacheConfig = clientConfig.getNearCacheConfig(name);
        nearCache = new ClientHeapNearCache(name, getContext(), nearCacheConfig);

        if (nearCache.isInvalidateOnChange()) {
            addNearCacheInvalidateListener();
        }
    }

    @Override
    protected boolean containsKeyInternal(Data keyData) {
        Object cached = nearCache.get(keyData);
        if (cached != null) {
            return NULL_OBJECT != cached;
        } else {
            return super.containsKeyInternal(keyData);
        }

    }

    @Override
    protected V getInternal(Data keyData) {
        Object cached = nearCache.get(keyData);
        if (cached != null) {
            if (NULL_OBJECT == cached) {
                return null;
            }
            return (V) cached;
        } else {
            V response = super.getInternal(keyData);

            nearCache.put(keyData, response);

            return response;
        }
    }

    @Override
    protected V removeInternal(Data keyData) {
        invalidateNearCache(keyData);
        return super.removeInternal(keyData);
    }

    @Override
    protected boolean removeInternal(Data keyData, Data valueData) {
        invalidateNearCache(keyData);
        return super.removeInternal(keyData, valueData);
    }

    @Override
    protected void deleteInternal(Data keyData) {
        invalidateNearCache(keyData);
        super.deleteInternal(keyData);
    }

    @Override
    public ICompletableFuture getAsyncInternal(final Data keyData) {
        Object cached = nearCache.get(keyData);
        if (cached != null && NULL_OBJECT != cached) {
            return new CompletedFuture(getContext().getSerializationService(),
                    cached, getContext().getExecutionService().getAsyncExecutor());
        }

        ICompletableFuture future = super.getAsyncInternal(keyData);
        future.andThen(new ExecutionCallback() {
            @Override
            public void onResponse(V response) {
                nearCache.put(keyData, response);
            }

            @Override
            public void onFailure(Throwable t) {

            }
        });
        return future;
    }

    @Override
    protected Future putAsyncInternal(long ttl, TimeUnit timeunit, Data keyData, Data valueData) {
        invalidateNearCache(keyData);
        return super.putAsyncInternal(ttl, timeunit, keyData, valueData);
    }

    @Override
    protected Future removeAsyncInternal(Data keyData) {
        invalidateNearCache(keyData);
        return super.removeAsyncInternal(keyData);
    }

    @Override
    protected Boolean tryRemoveInternal(long timeout, TimeUnit timeunit, Data keyData) {
        invalidateNearCache(keyData);
        return super.tryRemoveInternal(timeout, timeunit, keyData);
    }

    @Override
    protected Boolean tryPutInternal(long timeout, TimeUnit timeunit, Data keyData, Data valueData) {
        invalidateNearCache(keyData);
        return super.tryPutInternal(timeout, timeunit, keyData, valueData);
    }

    @Override
    protected V putInternal(long ttl, TimeUnit timeunit, Data keyData, Data valueData) {
        invalidateNearCache(keyData);
        return super.putInternal(ttl, timeunit, keyData, valueData);
    }

    @Override
    protected void putTransientInternal(long ttl, TimeUnit timeunit, Data keyData, Data valueData) {
        invalidateNearCache(keyData);
        super.putTransientInternal(ttl, timeunit, keyData, valueData);
    }

    @Override
    protected V putIfAbsentInternal(long ttl, TimeUnit timeunit, Data keyData, Data valueData) {
        invalidateNearCache(keyData);
        return super.putIfAbsentInternal(ttl, timeunit, keyData, valueData);
    }

    @Override
    protected Boolean replaceIfSameInternal(Data keyData, Data oldValueData, Data newValueData) {
        invalidateNearCache(keyData);
        return super.replaceIfSameInternal(keyData, oldValueData, newValueData);
    }

    @Override
    protected V replaceInternal(Data keyData, Data valueData) {
        invalidateNearCache(keyData);
        return super.replaceInternal(keyData, valueData);
    }

    @Override
    protected void setInternal(long ttl, TimeUnit timeunit, Data keyData, Data valueData) {
        invalidateNearCache(keyData);
        super.setInternal(ttl, timeunit, keyData, valueData);
    }

    @Override
    protected Boolean evictInternal(Data keyData) {
        invalidateNearCache(keyData);
        return super.evictInternal(keyData);
    }

    @Override
    public void evictAll() {
        nearCache.clear();
        super.evictAll();
    }

    @Override
    public void loadAll(boolean replaceExistingValues) {
        if (replaceExistingValues) {
            nearCache.clear();
        }
        super.loadAll(replaceExistingValues);
    }

    @Override
    protected void loadAllInternal(boolean replaceExistingValues, List dataKeys) {
        invalidateNearCache(dataKeys);
        super.loadAllInternal(replaceExistingValues, dataKeys);
    }

    @Override
    protected List getAllInternal(Map> partitionToKeyData, Map result) {
        for (Entry> partitionKeyEntry : partitionToKeyData.entrySet()) {
            List keyList = partitionKeyEntry.getValue();
            Iterator iterator = keyList.iterator();
            while (iterator.hasNext()) {
                Data key = iterator.next();
                Object cached = nearCache.get(key);
                if (cached != null && NULL_OBJECT != cached) {
                    result.put((K) toObject(key), (V) cached);
                    iterator.remove();
                }
            }
        }

        List responses = super.getAllInternal(partitionToKeyData, result);
        for (MapEntries entries : responses) {
            for (Entry entry : entries.entries()) {
                nearCache.put(entry.getKey(), entry.getValue());
            }
        }
        //TODO: This returned value is not be used.
        return responses;
    }

    @Override
    public LocalMapStats getLocalMapStats() {
        LocalMapStats localMapStats = super.getLocalMapStats();
        NearCacheStats nearCacheStats = nearCache.getNearCacheStats();
        ((LocalMapStatsImpl) localMapStats).setNearCacheStats(((NearCacheStatsImpl) nearCacheStats));
        return localMapStats;
    }

    @Override
    public Object executeOnKeyInternal(Data keyData, EntryProcessor entryProcessor) {
        invalidateNearCache(keyData);
        return super.executeOnKeyInternal(keyData, entryProcessor);
    }

    @Override
    public Future submitToKeyInternal(Data keyData, EntryProcessor entryProcessor) {
        invalidateNearCache(keyData);
        return super.submitToKeyInternal(keyData, entryProcessor);
    }

    @Override
    public void submitToKeyInternal(Data keyData, EntryProcessor entryProcessor, ExecutionCallback callback) {
        invalidateNearCache(keyData);
        super.submitToKeyInternal(keyData, entryProcessor, callback);
    }

    @Override
    protected Map prepareResult(MapEntries mapEntries) {
        if (mapEntries.isEmpty()) {
            return emptyMap();
        }

        Map result = createHashMap(mapEntries.size());
        for (Entry dataEntry : mapEntries) {
            Data keyData = dataEntry.getKey();
            invalidateNearCache(keyData);
            Data valueData = dataEntry.getValue();
            K key = toObject(keyData);
            result.put(key, toObject(valueData));
        }
        return result;
    }

    @Override
    protected void putAllInternal(List> futures, MapEntries[] entriesPerPartition) {
        for (int i = 0; i < entriesPerPartition.length; i++) {
            MapEntries mapEntries = entriesPerPartition[i];
            if (mapEntries != null) {
                Collection> entries = mapEntries.entries();
                for (Entry entry : entries) {
                    Data keyData = entry.getKey();
                    invalidateNearCache(keyData);
                }
            }
        }
        super.putAllInternal(futures, entriesPerPartition);
    }

    @Override
    public void clear() {
        nearCache.clear();
        super.clear();
    }

    @Override
    protected void onDestroy() {
        removeNearCacheInvalidationListener();
        nearCache.destroy();

        super.onDestroy();
    }

    @Override
    protected void onShutdown() {
        removeNearCacheInvalidationListener();
        nearCache.destroy();

        super.onShutdown();
    }

    public NearCache getNearCache() {
        return nearCache;
    }

    protected void invalidateNearCache(Data key) {
        nearCache.remove(key);
    }


    protected void invalidateNearCache(Collection keys) {
        if (keys == null || keys.isEmpty()) {
            return;
        }
        for (Data key : keys) {
            nearCache.remove(key);
        }
    }

    protected void addNearCacheInvalidateListener() {
        EventHandler handler = new InvalidationListener();
        addNearCacheInvalidateListener(handler);
    }

    public void addNearCacheInvalidateListener(EventHandler handler) {
        try {
            MapAddNearCacheEntryListenerRequest addRequest = new MapAddNearCacheEntryListenerRequest(name, false);
            BaseClientRemoveListenerRequest removeRequest = new MapRemoveEntryListenerRequest(name);

            invalidationListenerId = registerListener(addRequest, removeRequest, handler);
        } catch (Exception e) {
            Logger.getLogger(ClientHeapNearCache.class).severe(
                    "-----------------\n Near Cache is not initialized!!! \n-----------------", e);
        }
    }

    protected final class InvalidationListener implements EventHandler {

        public InvalidationListener() {
        }

        @Override
        public void handle(Object event) {
            if (event instanceof BatchNearCacheInvalidation) {
                List invalidations = ((BatchNearCacheInvalidation) event).getInvalidations();
                for (SingleNearCacheInvalidation invalidation : invalidations) {
                    nearCache.remove(invalidation.getKey());
                }
                return;
            }

            if (event instanceof SingleNearCacheInvalidation) {
                Data key = ((SingleNearCacheInvalidation) event).getKey();
                nearCache.remove(key);
                return;
            }

            if (event instanceof CleaningNearCacheInvalidation) {
                nearCache.clear();
                return;
            }

            throw new IllegalArgumentException("Unexpected event received [" + event + ']');
        }

        @Override
        public void beforeListenerRegister() {
            nearCache.clear();
        }

        @Override
        public void onListenerRegister() {
            nearCache.clear();
        }
    }

    protected void removeNearCacheInvalidationListener() {
        String invalidationListenerId = this.invalidationListenerId;
        if (invalidationListenerId == null) {
            return;
        }

        deregisterListener(invalidationListenerId);
    }
}