com.hazelcast.cache.impl.CacheProxy Maven / Gradle / Ivy
/*
* Copyright (c) 2008-2020, 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.cache.impl;
import com.hazelcast.cache.CacheStatistics;
import com.hazelcast.cache.EventJournalCacheEvent;
import com.hazelcast.cache.impl.event.CachePartitionLostEventFilter;
import com.hazelcast.cache.impl.event.CachePartitionLostListener;
import com.hazelcast.cache.impl.event.InternalCachePartitionLostListenerAdapter;
import com.hazelcast.cache.impl.journal.CacheEventJournalReadOperation;
import com.hazelcast.cache.impl.journal.CacheEventJournalSubscribeOperation;
import com.hazelcast.config.CacheConfig;
import com.hazelcast.internal.config.CacheConfigReadOnly;
import com.hazelcast.internal.journal.EventJournalInitialSubscriberState;
import com.hazelcast.internal.journal.EventJournalReader;
import com.hazelcast.internal.util.Clock;
import com.hazelcast.internal.util.collection.PartitionIdSet;
import com.hazelcast.map.impl.MapEntries;
import com.hazelcast.internal.serialization.Data;
import com.hazelcast.ringbuffer.ReadResultSet;
import com.hazelcast.spi.impl.InternalCompletableFuture;
import com.hazelcast.spi.impl.NodeEngine;
import com.hazelcast.spi.impl.eventservice.EventFilter;
import com.hazelcast.spi.impl.eventservice.EventRegistration;
import com.hazelcast.spi.impl.operationservice.Operation;
import com.hazelcast.spi.impl.operationservice.OperationFactory;
import com.hazelcast.spi.impl.operationservice.OperationService;
import com.hazelcast.spi.impl.operationservice.impl.InvocationFuture;
import javax.cache.CacheException;
import javax.cache.configuration.CacheEntryListenerConfiguration;
import javax.cache.configuration.Configuration;
import javax.cache.expiry.ExpiryPolicy;
import javax.cache.integration.CompletionListener;
import javax.cache.processor.EntryProcessor;
import javax.cache.processor.EntryProcessorException;
import javax.cache.processor.EntryProcessorResult;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Future;
import java.util.function.Function;
import java.util.function.Predicate;
import static com.hazelcast.cache.impl.CacheProxyUtil.validateNotNull;
import static com.hazelcast.internal.util.ExceptionUtil.rethrow;
import static com.hazelcast.internal.util.ExceptionUtil.rethrowAllowedTypeFirst;
import static com.hazelcast.internal.util.MapUtil.createHashMap;
import static com.hazelcast.internal.util.MapUtil.toIntSize;
import static com.hazelcast.internal.util.Preconditions.checkNotNull;
import static com.hazelcast.internal.util.SetUtil.createHashSet;
import static java.util.Collections.emptyMap;
/**
* ICache implementation
*
* This proxy is the implementation of ICache and javax.cache.Cache which is returned by
* HazelcastServerCacheManager. It represents a cache for server or embedded mode.
*
*
* Each cache method actually is an operation which is sent to related partition(s) or node(s).
* Operations are executed on partition's or node's executor pools and the results are delivered to the user.
*
*
* In order to access a {@linkplain CacheProxy} by name, a cacheManager should be used. It's advised to use
* {@link com.hazelcast.cache.ICache} instead.
*
*
* @param the type of key.
* @param the type of value.
*/
@SuppressWarnings({"checkstyle:methodcount", "checkstyle:classfanoutcomplexity"})
public class CacheProxy extends CacheProxySupport
implements EventJournalReader> {
private static final CacheStatistics EMPTY_CACHE_STATS = new CacheStatisticsImpl(Clock.currentTimeMillis());
CacheProxy(CacheConfig cacheConfig, NodeEngine nodeEngine, ICacheService cacheService) {
super(cacheConfig, nodeEngine, cacheService);
}
@Override
public V get(K key) {
return get(key, null);
}
@Override
public Map getAll(Set keys) {
return getAll(keys, null);
}
@Override
public boolean containsKey(K key) {
ensureOpen();
validateNotNull(key);
Data dataKey = serializationService.toData(key);
Operation operation = operationProvider.createContainsKeyOperation(dataKey);
OperationService operationService = getNodeEngine().getOperationService();
int partitionId = getPartitionId(dataKey);
InvocationFuture future = operationService.invokeOnPartition(getServiceName(), operation, partitionId);
return future.joinInternal();
}
@Override
public void loadAll(Set keys, boolean replaceExistingValues, CompletionListener completionListener) {
ensureOpen();
validateNotNull(keys);
for (K key : keys) {
CacheProxyUtil.validateConfiguredTypes(cacheConfig, key);
}
Set keysData = createHashSet(keys.size());
for (K key : keys) {
validateNotNull(key);
keysData.add(serializationService.toData(key));
}
createAndSubmitLoadAllTask(keysData, replaceExistingValues, completionListener);
}
@Override
public void put(K key, V value) {
put(key, value, null);
}
@Override
public V getAndPut(K key, V value) {
return getAndPut(key, value, null);
}
@Override
public void putAll(Map map) {
putAll(map, null);
}
@Override
public boolean putIfAbsent(K key, V value) {
return putIfAbsent(key, value, null);
}
@Override
public boolean remove(K key) {
try {
InternalCompletableFuture future = removeAsyncInternal(key, null, false, false, true);
return future.get();
} catch (Throwable e) {
throw rethrowAllowedTypeFirst(e, CacheException.class);
}
}
@Override
public boolean remove(K key, V oldValue) {
try {
InternalCompletableFuture future = removeAsyncInternal(key, oldValue, true, false, true);
return future.get();
} catch (Throwable e) {
throw rethrowAllowedTypeFirst(e, CacheException.class);
}
}
@Override
public V getAndRemove(K key) {
try {
InternalCompletableFuture future = removeAsyncInternal(key, null, false, true, true);
return future.get();
} catch (Throwable e) {
throw rethrowAllowedTypeFirst(e, CacheException.class);
}
}
@Override
public boolean replace(K key, V oldValue, V newValue) {
return replace(key, oldValue, newValue, null);
}
@Override
public boolean replace(K key, V value) {
return replace(key, value, (ExpiryPolicy) null);
}
@Override
public V getAndReplace(K key, V value) {
return getAndReplace(key, value, null);
}
@Override
public void removeAll(Set keys) {
ensureOpen();
validateNotNull(keys);
if (keys.isEmpty()) {
return;
}
removeAllInternal(keys);
}
@Override
public void removeAll() {
ensureOpen();
removeAllInternal(null);
}
@Override
public void clear() {
ensureOpen();
clearInternal();
}
@Override
public > C getConfiguration(Class clazz) {
if (clazz.isInstance(cacheConfig)) {
return clazz.cast(new CacheConfigReadOnly<>(cacheConfig));
}
throw new IllegalArgumentException("The configuration class " + clazz + " is not supported by this implementation");
}
@Override
public T invoke(K key, EntryProcessor entryProcessor, Object... arguments) throws EntryProcessorException {
ensureOpen();
validateNotNull(key);
checkNotNull(entryProcessor, "Entry Processor is null");
Data keyData = serializationService.toData(key);
return invokeInternal(keyData, entryProcessor, arguments);
}
@Override
public Map> invokeAll(Set keys, EntryProcessor entryProcessor,
Object... arguments) {
// TODO: implement a multiple (batch) invoke operation and its factory
ensureOpen();
validateNotNull(keys);
checkNotNull(entryProcessor, "Entry Processor is null");
Map> allResult = createHashMap(keys.size());
for (K key : keys) {
validateNotNull(key);
CacheEntryProcessorResult ceResult;
try {
T result = invoke(key, entryProcessor, arguments);
ceResult = result != null ? new CacheEntryProcessorResult(result) : null;
} catch (Exception e) {
ceResult = new CacheEntryProcessorResult(e);
}
if (ceResult != null) {
allResult.put(key, ceResult);
}
}
return allResult;
}
@Override
public T unwrap(Class clazz) {
if (clazz.isAssignableFrom(((Object) this).getClass())) {
return clazz.cast(this);
}
throw new IllegalArgumentException("Unwrapping to " + clazz + " is not supported by this implementation");
}
@Override
public void registerCacheEntryListener(CacheEntryListenerConfiguration cacheEntryListenerConfiguration) {
registerCacheEntryListener(cacheEntryListenerConfiguration, true);
}
@Override
public void registerCacheEntryListener(CacheEntryListenerConfiguration cacheEntryListenerConfiguration,
boolean addToConfig) {
ensureOpen();
checkNotNull(cacheEntryListenerConfiguration, "CacheEntryListenerConfiguration can't be null");
CacheEventListenerAdaptor entryListener = new CacheEventListenerAdaptor(this,
cacheEntryListenerConfiguration,
getNodeEngine().getSerializationService());
UUID regId = getService().registerListener(getDistributedObjectName(), entryListener, entryListener);
if (regId != null) {
if (addToConfig) {
cacheConfig.addCacheEntryListenerConfiguration(cacheEntryListenerConfiguration);
}
addListenerLocally(regId, cacheEntryListenerConfiguration);
if (addToConfig) {
updateCacheListenerConfigOnOtherNodes(cacheEntryListenerConfiguration, true);
}
}
}
@Override
public void deregisterCacheEntryListener(CacheEntryListenerConfiguration cacheEntryListenerConfiguration) {
checkNotNull(cacheEntryListenerConfiguration, "CacheEntryListenerConfiguration can't be null");
UUID regId = getListenerIdLocal(cacheEntryListenerConfiguration);
if (regId != null) {
if (getService().deregisterListener(getDistributedObjectName(), regId)) {
removeListenerLocally(cacheEntryListenerConfiguration);
cacheConfig.removeCacheEntryListenerConfiguration(cacheEntryListenerConfiguration);
updateCacheListenerConfigOnOtherNodes(cacheEntryListenerConfiguration, false);
}
}
}
@Override
public Iterator> iterator() {
ensureOpen();
return new CachePartitionsIterator<>(this, false);
}
@Override
public Iterator> iterator(int fetchSize) {
ensureOpen();
return new CachePartitionsIterator<>(this, fetchSize, false);
}
@Override
public Iterator> iterator(int fetchSize, int partitionId, boolean prefetchValues) {
ensureOpen();
return new CachePartitionIterator<>(this, fetchSize, partitionId, prefetchValues);
}
@Override
public UUID addPartitionLostListener(CachePartitionLostListener listener) {
checkNotNull(listener, "CachePartitionLostListener can't be null");
EventFilter filter = new CachePartitionLostEventFilter();
InternalCachePartitionLostListenerAdapter listenerAdapter = new InternalCachePartitionLostListenerAdapter(listener);
injectDependencies(listener);
EventRegistration registration = getService().getNodeEngine().getEventService()
.registerListener(AbstractCacheService.SERVICE_NAME, name, filter, listenerAdapter);
return registration.getId();
}
@Override
public boolean removePartitionLostListener(UUID id) {
checkNotNull(id, "Listener ID should not be null!");
return getService().getNodeEngine().getEventService()
.deregisterListener(AbstractCacheService.SERVICE_NAME, name, id);
}
@Override
public CompletionStage subscribeToEventJournal(int partitionId) {
final CacheEventJournalSubscribeOperation op = new CacheEventJournalSubscribeOperation(nameWithPrefix);
op.setPartitionId(partitionId);
return getNodeEngine().getOperationService().invokeOnPartition(op);
}
@Override
public CompletionStage> readFromEventJournal(
long startSequence,
int minSize,
int maxSize,
int partitionId,
Predicate> predicate,
Function, ? extends T> projection
) {
if (maxSize < minSize) {
throw new IllegalArgumentException("maxSize " + maxSize
+ " must be greater or equal to minSize " + minSize);
}
final CacheEventJournalReadOperation op = new CacheEventJournalReadOperation<>(
nameWithPrefix, startSequence, minSize, maxSize, predicate, projection);
op.setPartitionId(partitionId);
return getNodeEngine().getOperationService().invokeOnPartition(op);
}
@Override
public CacheStatistics getLocalCacheStatistics() {
if (!cacheConfig.isStatisticsEnabled()) {
return EMPTY_CACHE_STATS;
}
return getService().createCacheStatIfAbsent(cacheConfig.getNameWithPrefix());
}
@Override
public InternalCompletableFuture getAsync(K key) {
return getAsync(key, null);
}
@Override
public InternalCompletableFuture getAsync(K key, ExpiryPolicy expiryPolicy) {
ensureOpen();
validateNotNull(key);
Data keyData = serializationService.toData(key);
Operation op = operationProvider.createGetOperation(keyData, expiryPolicy);
return invoke(op, keyData, false);
}
@Override
public InternalCompletableFuture putAsync(K key, V value) {
return putAsync(key, value, null);
}
@Override
public InternalCompletableFuture putAsync(K key, V value, ExpiryPolicy expiryPolicy) {
return putAsyncInternal(key, value, expiryPolicy, false, false);
}
@Override
public InternalCompletableFuture putIfAbsentAsync(K key, V value) {
return putIfAbsentAsyncInternal(key, value, null, false);
}
@Override
public InternalCompletableFuture putIfAbsentAsync(K key, V value, ExpiryPolicy expiryPolicy) {
return putIfAbsentAsyncInternal(key, value, expiryPolicy, false);
}
@Override
public InternalCompletableFuture getAndPutAsync(K key, V value) {
return getAndPutAsync(key, value, null);
}
@Override
public InternalCompletableFuture getAndPutAsync(K key, V value, ExpiryPolicy expiryPolicy) {
return putAsyncInternal(key, value, expiryPolicy, true, false);
}
@Override
public InternalCompletableFuture removeAsync(K key) {
return removeAsyncInternal(key, null, false, false, false);
}
@Override
public InternalCompletableFuture removeAsync(K key, V oldValue) {
return removeAsyncInternal(key, oldValue, true, false, false);
}
@Override
public InternalCompletableFuture getAndRemoveAsync(K key) {
return removeAsyncInternal(key, null, false, true, false);
}
@Override
public InternalCompletableFuture replaceAsync(K key, V value) {
return replaceAsyncInternal(key, null, value, null, false, false, false);
}
@Override
public InternalCompletableFuture replaceAsync(K key, V value, ExpiryPolicy expiryPolicy) {
return replaceAsyncInternal(key, null, value, expiryPolicy, false, false, false);
}
@Override
public InternalCompletableFuture replaceAsync(K key, V oldValue, V newValue) {
return replaceAsyncInternal(key, oldValue, newValue, null, true, false, false);
}
@Override
public InternalCompletableFuture replaceAsync(K key, V oldValue, V newValue, ExpiryPolicy expiryPolicy) {
return replaceAsyncInternal(key, oldValue, newValue, expiryPolicy, true, false, false);
}
@Override
public InternalCompletableFuture getAndReplaceAsync(K key, V value) {
return replaceAsyncInternal(key, null, value, null, false, true, false);
}
@Override
public InternalCompletableFuture getAndReplaceAsync(K key, V value, ExpiryPolicy expiryPolicy) {
return replaceAsyncInternal(key, null, value, expiryPolicy, false, true, false);
}
@Override
public V get(K key, ExpiryPolicy expiryPolicy) {
try {
Future future = getAsync(key, expiryPolicy);
return future.get();
} catch (Throwable e) {
throw rethrowAllowedTypeFirst(e, CacheException.class);
}
}
@Override
public Map getAll(Set keys, ExpiryPolicy expiryPolicy) {
ensureOpen();
validateNotNull(keys);
if (keys.isEmpty()) {
return emptyMap();
}
final int keyCount = keys.size();
final Set ks = createHashSet(keyCount);
for (K key : keys) {
validateNotNull(key);
Data dataKey = serializationService.toData(key);
ks.add(dataKey);
}
Map result = createHashMap(keyCount);
PartitionIdSet partitions = getPartitionsForKeys(ks);
try {
OperationFactory factory = operationProvider.createGetAllOperationFactory(ks, expiryPolicy);
OperationService operationService = getNodeEngine().getOperationService();
Map responses = operationService.invokeOnPartitions(getServiceName(), factory, partitions);
for (Object response : responses.values()) {
MapEntries mapEntries = serializationService.toObject(response);
mapEntries.putAllToMap(serializationService, result);
}
} catch (Throwable e) {
throw rethrowAllowedTypeFirst(e, CacheException.class);
}
return result;
}
@Override
public void put(K key, V value, ExpiryPolicy expiryPolicy) {
try {
InternalCompletableFuture