
com.hazelcast.cache.impl.AbstractCacheService Maven / Gradle / Ivy
/*
* Copyright (c) 2008-2016, 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.impl.event.CachePartitionLostEventFilter;
import com.hazelcast.cache.impl.operation.CacheDestroyOperation;
import com.hazelcast.cache.impl.operation.PostJoinCacheOperation;
import com.hazelcast.config.CacheConfig;
import com.hazelcast.config.CacheSimpleConfig;
import com.hazelcast.config.InMemoryFormat;
import com.hazelcast.core.DistributedObject;
import com.hazelcast.core.Member;
import com.hazelcast.nio.IOUtil;
import com.hazelcast.nio.serialization.Data;
import com.hazelcast.partition.InternalPartitionLostEvent;
import com.hazelcast.partition.MigrationEndpoint;
import com.hazelcast.spi.EventFilter;
import com.hazelcast.spi.EventRegistration;
import com.hazelcast.spi.EventService;
import com.hazelcast.spi.NodeEngine;
import com.hazelcast.spi.Operation;
import com.hazelcast.spi.OperationService;
import com.hazelcast.spi.PartitionAwareService;
import com.hazelcast.spi.PartitionMigrationEvent;
import com.hazelcast.spi.PostJoinAwareService;
import com.hazelcast.spi.QuorumAwareService;
import com.hazelcast.spi.SplitBrainHandlerService;
import com.hazelcast.util.Clock;
import com.hazelcast.util.ConcurrencyUtil;
import com.hazelcast.util.ConstructorFunction;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import javax.cache.configuration.CacheEntryListenerConfiguration;
import javax.cache.event.CacheEntryListener;
import java.io.Closeable;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public abstract class AbstractCacheService
implements ICacheService,
PostJoinAwareService,
PartitionAwareService,
QuorumAwareService,
SplitBrainHandlerService {
protected final ConcurrentMap configs = new ConcurrentHashMap();
protected final ConcurrentMap cacheContexts = new ConcurrentHashMap();
protected final ConcurrentMap statistics = new ConcurrentHashMap();
protected final ConcurrentMap> resources = new ConcurrentHashMap>();
protected final ConcurrentMap closeableListeners = new ConcurrentHashMap();
protected final ConcurrentMap operationProviderCache =
new ConcurrentHashMap();
protected final ConstructorFunction cacheContexesConstructorFunction =
new ConstructorFunction() {
@Override
public CacheContext createNew(String name) {
return new CacheContext();
}
};
protected final ConstructorFunction cacheStatisticsConstructorFunction =
new ConstructorFunction() {
@Override
public CacheStatisticsImpl createNew(String name) {
return new CacheStatisticsImpl(
Clock.currentTimeMillis(),
CacheEntryCountResolver.createEntryCountResolver(getOrCreateCacheContext(name)));
}
};
protected NodeEngine nodeEngine;
protected CachePartitionSegment[] segments;
protected CacheEventHandler cacheEventHandler;
protected CacheSplitBrainHandler cacheSplitBrainHandler;
@Override
public final void init(NodeEngine nodeEngine, Properties properties) {
this.nodeEngine = nodeEngine;
int partitionCount = nodeEngine.getPartitionService().getPartitionCount();
this.segments = new CachePartitionSegment[partitionCount];
for (int i = 0; i < partitionCount; i++) {
segments[i] = newPartitionSegment(i);
}
this.cacheEventHandler = new CacheEventHandler(nodeEngine);
this.cacheSplitBrainHandler = new CacheSplitBrainHandler(nodeEngine, configs, segments);
postInit(nodeEngine, properties);
}
protected void postInit(NodeEngine nodeEngine, Properties properties) {
}
protected abstract CachePartitionSegment newPartitionSegment(int partitionId);
protected abstract ICacheRecordStore createNewRecordStore(String name, int partitionId);
@Override
public void reset() {
reset(false);
}
private void reset(boolean onShutdown) {
for (String objectName : configs.keySet()) {
deleteCache(objectName, true, null, false);
}
final CachePartitionSegment[] partitionSegments = segments;
for (CachePartitionSegment partitionSegment : partitionSegments) {
if (partitionSegment != null) {
if (onShutdown) {
partitionSegment.shutdown();
} else {
partitionSegment.clear();
partitionSegment.init();
}
}
}
}
@Override
public void shutdown(boolean terminate) {
if (!terminate) {
cacheEventHandler.shutdown();
reset(true);
}
}
@Override
public DistributedObject createDistributedObject(String objectName) {
return new CacheDistributedObject(objectName, nodeEngine, this);
}
@Override
public void destroyDistributedObject(String objectName) {
}
@Override
public void beforeMigration(PartitionMigrationEvent event) {
}
@Override
public void commitMigration(PartitionMigrationEvent event) {
if (event.getMigrationEndpoint() == MigrationEndpoint.SOURCE) {
clearPartitionReplica(event.getPartitionId());
}
initPartitionReplica(event.getPartitionId());
}
@Override
public void rollbackMigration(PartitionMigrationEvent event) {
if (event.getMigrationEndpoint() == MigrationEndpoint.DESTINATION) {
clearPartitionReplica(event.getPartitionId());
}
initPartitionReplica(event.getPartitionId());
}
private void initPartitionReplica(int partitionId) {
segments[partitionId].init();
}
@Override
public void clearPartitionReplica(int partitionId) {
segments[partitionId].clear();
}
@Override
public ICacheRecordStore getOrCreateRecordStore(String name, int partitionId) {
return segments[partitionId].getOrCreateRecordStore(name);
}
@Override
public ICacheRecordStore getRecordStore(String name, int partitionId) {
return segments[partitionId].getRecordStore(name);
}
@Override
public CachePartitionSegment getSegment(int partitionId) {
return segments[partitionId];
}
protected void destroySegments(String name) {
for (CachePartitionSegment segment : segments) {
segment.deleteRecordStore(name, true);
}
}
protected void closeSegments(String name) {
for (CachePartitionSegment segment : segments) {
segment.deleteRecordStore(name, false);
}
}
@Override
public void deleteCache(String name, boolean isLocal, String callerUuid, boolean destroy) {
CacheConfig config = deleteCacheConfig(name);
if (destroy) {
destroySegments(name);
} else {
closeSegments(name);
}
if (!isLocal) {
deregisterAllListener(name);
cacheContexts.remove(name);
}
operationProviderCache.remove(name);
setStatisticsEnabled(config, name, false);
setManagementEnabled(config, name, false);
deleteCacheConfig(name);
deleteCacheStat(name);
deleteCacheResources(name);
if (!isLocal) {
destroyCacheOnAllMembers(name, callerUuid);
}
}
protected void destroyCacheOnAllMembers(String name, String callerUuid) {
final OperationService operationService = nodeEngine.getOperationService();
final Collection members = nodeEngine.getClusterService().getMembers();
for (Member member : members) {
if (!member.localMember() && !member.getUuid().equals(callerUuid)) {
final CacheDestroyOperation op = new CacheDestroyOperation(name, true);
operationService.invokeOnTarget(AbstractCacheService.SERVICE_NAME, op, member.getAddress());
}
}
}
@Override
public CacheConfig putCacheConfigIfAbsent(CacheConfig config) {
final CacheConfig localConfig = configs.putIfAbsent(config.getNameWithPrefix(), config);
if (localConfig == null) {
if (config.isStatisticsEnabled()) {
setStatisticsEnabled(config, config.getNameWithPrefix(), true);
}
if (config.isManagementEnabled()) {
setManagementEnabled(config, config.getNameWithPrefix(), true);
}
}
return localConfig;
}
@Override
public CacheConfig deleteCacheConfig(String name) {
return configs.remove(name);
}
@Override
public CacheStatisticsImpl createCacheStatIfAbsent(String name) {
return ConcurrencyUtil.getOrPutIfAbsent(statistics, name, cacheStatisticsConstructorFunction);
}
public CacheContext getCacheContext(String name) {
return cacheContexts.get(name);
}
@Override
public CacheContext getOrCreateCacheContext(String name) {
return ConcurrencyUtil.getOrPutIfAbsent(cacheContexts, name, cacheContexesConstructorFunction);
}
@Override
public void deleteCacheStat(String name) {
statistics.remove(name);
}
@Override
public void setStatisticsEnabled(CacheConfig cacheConfig, String cacheNameWithPrefix, boolean enabled) {
cacheConfig = cacheConfig != null ? cacheConfig : configs.get(cacheNameWithPrefix);
if (cacheConfig != null) {
final String cacheManagerName = cacheConfig.getUriString();
cacheConfig.setStatisticsEnabled(enabled);
if (enabled) {
final CacheStatisticsImpl cacheStatistics = createCacheStatIfAbsent(cacheNameWithPrefix);
final CacheStatisticsMXBeanImpl mxBean = new CacheStatisticsMXBeanImpl(cacheStatistics);
MXBeanUtil.registerCacheObject(mxBean, cacheManagerName, cacheConfig.getName(), true);
} else {
MXBeanUtil.unregisterCacheObject(cacheManagerName, cacheConfig.getName(), true);
deleteCacheStat(cacheNameWithPrefix);
}
}
}
@Override
public void setManagementEnabled(CacheConfig cacheConfig, String cacheNameWithPrefix, boolean enabled) {
cacheConfig = cacheConfig != null ? cacheConfig : configs.get(cacheNameWithPrefix);
if (cacheConfig != null) {
final String cacheManagerName = cacheConfig.getUriString();
cacheConfig.setManagementEnabled(enabled);
if (enabled) {
final CacheMXBeanImpl mxBean = new CacheMXBeanImpl(cacheConfig);
MXBeanUtil.registerCacheObject(mxBean, cacheManagerName, cacheConfig.getName(), false);
} else {
MXBeanUtil.unregisterCacheObject(cacheManagerName, cacheConfig.getName(), false);
deleteCacheStat(cacheNameWithPrefix);
}
}
}
@Override
public CacheConfig getCacheConfig(String name) {
return configs.get(name);
}
@Override
public CacheSimpleConfig findCacheConfig(String simpleName) {
if (simpleName == null) {
return null;
}
return nodeEngine.getConfig().findCacheConfig(simpleName);
}
public Collection getCacheConfigs() {
return configs.values();
}
public Object toObject(Object data) {
if (data == null) {
return null;
}
if (data instanceof Data) {
return nodeEngine.toObject(data);
} else {
return data;
}
}
public Data toData(Object object) {
if (object == null) {
return null;
}
if (object instanceof Data) {
return (Data) object;
} else {
return nodeEngine.getSerializationService().toData(object);
}
}
@Override
public void publishEvent(CacheEventContext cacheEventContext) {
cacheEventHandler.publishEvent(cacheEventContext);
}
@Override
public void publishEvent(String cacheName, CacheEventSet eventSet, int orderKey) {
cacheEventHandler.publishEvent(cacheName, eventSet, orderKey);
}
@Override
public NodeEngine getNodeEngine() {
return nodeEngine;
}
@Override
public void dispatchEvent(Object event, CacheEventListener listener) {
listener.handleEvent(event);
}
@Override
public String registerListener(String name, CacheEventListener listener, boolean isLocal) {
return registerListenerInternal(name, listener, null, isLocal);
}
@Override
public String registerListener(String name, CacheEventListener listener, EventFilter eventFilter, boolean isLocal) {
return registerListenerInternal(name, listener, eventFilter, isLocal);
}
protected String registerListenerInternal(String name, CacheEventListener listener,
EventFilter eventFilter, boolean isLocal) {
EventService eventService = getNodeEngine().getEventService();
EventRegistration reg;
if (isLocal) {
if (eventFilter == null) {
reg = eventService.registerLocalListener(AbstractCacheService.SERVICE_NAME, name, listener);
} else {
reg = eventService.registerLocalListener(AbstractCacheService.SERVICE_NAME, name, eventFilter, listener);
}
} else {
if (eventFilter == null) {
reg = eventService.registerListener(AbstractCacheService.SERVICE_NAME, name, listener);
} else {
reg = eventService.registerListener(AbstractCacheService.SERVICE_NAME, name, eventFilter, listener);
}
}
final String id = reg.getId();
if (listener instanceof Closeable) {
closeableListeners.put(id, (Closeable) listener);
} else if (listener instanceof CacheEntryListenerProvider) {
CacheEntryListener cacheEntryListener = ((CacheEntryListenerProvider) listener).getCacheEntryListener();
if (cacheEntryListener instanceof Closeable) {
closeableListeners.put(id, (Closeable) cacheEntryListener);
}
}
return id;
}
@Override
public boolean deregisterListener(String name, String registrationId) {
final EventService eventService = getNodeEngine().getEventService();
boolean result = eventService.deregisterListener(SERVICE_NAME, name, registrationId);
Closeable listener = closeableListeners.remove(registrationId);
if (listener != null) {
IOUtil.closeResource(listener);
}
return result;
}
@Override
public void deregisterAllListener(String name) {
final EventService eventService = getNodeEngine().getEventService();
final Collection registrations = eventService.getRegistrations(SERVICE_NAME, name);
if (registrations != null) {
for (EventRegistration registration : registrations) {
Closeable listener = closeableListeners.remove(registration.getId());
if (listener != null) {
IOUtil.closeResource(listener);
}
}
}
eventService.deregisterAllListeners(AbstractCacheService.SERVICE_NAME, name);
CacheContext cacheContext = cacheContexts.get(name);
if (cacheContext != null) {
cacheContext.resetCacheEntryListenerCount();
cacheContext.resetInvalidationListenerCount();
}
}
@Override
public CacheStatisticsImpl getStatistics(String name) {
return statistics.get(name);
}
@Override
public CacheOperationProvider getCacheOperationProvider(String nameWithPrefix, InMemoryFormat inMemoryFormat) {
if (InMemoryFormat.NATIVE.equals(inMemoryFormat)) {
throw new IllegalArgumentException("Native memory is available only in Hazelcast Enterprise."
+ "Make sure you have Hazelcast Enterprise JARs on your classpath!");
}
CacheOperationProvider cacheOperationProvider = operationProviderCache.get(nameWithPrefix);
if (cacheOperationProvider != null) {
return cacheOperationProvider;
}
cacheOperationProvider = createOperationProvider(nameWithPrefix, inMemoryFormat);
CacheOperationProvider current = operationProviderCache.putIfAbsent(nameWithPrefix, cacheOperationProvider);
return current == null ? cacheOperationProvider : current;
}
protected abstract CacheOperationProvider createOperationProvider(String nameWithPrefix, InMemoryFormat inMemoryFormat);
@SuppressFBWarnings(value = "JLM_JSR166_UTILCONCURRENT_MONITORENTER", justification =
"several ops performed on concurrent map, need synchronization for atomicity")
public void addCacheResource(String name, Closeable resource) {
Set cacheResources = resources.get(name);
if (cacheResources == null) {
synchronized (resources) {
// In case of creation of resource set for specified cache name, we checks double from resources map.
// But this happens only once for each cache and this prevents other calls
// from unnecessary "synchronized" lock on "resources" instance
// since "cacheResources" will not be for specified cache name.
cacheResources = resources.get(name);
if (cacheResources == null) {
cacheResources = Collections.newSetFromMap(new ConcurrentHashMap());
resources.put(name, cacheResources);
}
}
}
cacheResources.add(resource);
}
private void deleteCacheResources(String name) {
Set cacheResources = resources.remove(name);
if (cacheResources != null) {
for (Closeable resource : cacheResources) {
IOUtil.closeResource(resource);
}
cacheResources.clear();
}
}
@Override
public Operation getPostJoinOperation() {
PostJoinCacheOperation postJoinCacheOperation = new PostJoinCacheOperation();
for (Map.Entry cacheConfigEntry : configs.entrySet()) {
postJoinCacheOperation.addCacheConfig(cacheConfigEntry.getValue());
}
return postJoinCacheOperation;
}
protected void publishCachePartitionLostEvent(String cacheName, int partitionId) {
final Collection registrations = new LinkedList();
for (EventRegistration registration : getRegistrations(cacheName)) {
if (registration.getFilter() instanceof CachePartitionLostEventFilter) {
registrations.add(registration);
}
}
if (registrations.isEmpty()) {
return;
}
final Member member = nodeEngine.getLocalMember();
final CacheEventData eventData = new CachePartitionEventData(cacheName, partitionId, member);
final EventService eventService = nodeEngine.getEventService();
eventService.publishEvent(SERVICE_NAME, registrations, eventData, partitionId);
}
Collection getRegistrations(String cacheName) {
final EventService eventService = nodeEngine.getEventService();
return eventService.getRegistrations(SERVICE_NAME, cacheName);
}
@Override
public void onPartitionLost(InternalPartitionLostEvent partitionLostEvent) {
final int partitionId = partitionLostEvent.getPartitionId();
for (CacheConfig config : getCacheConfigs()) {
final String cacheName = config.getName();
if (config.getTotalBackupCount() <= partitionLostEvent.getLostReplicaIndex()) {
publishCachePartitionLostEvent(cacheName, partitionId);
}
}
}
public void cacheEntryListenerRegistered(String name,
CacheEntryListenerConfiguration cacheEntryListenerConfiguration) {
CacheConfig cacheConfig = getCacheConfig(name);
if (cacheConfig == null) {
throw new IllegalStateException("CacheConfig does not exist for cache " + name);
}
cacheConfig.addCacheEntryListenerConfiguration(cacheEntryListenerConfiguration);
}
public void cacheEntryListenerDeregistered(String name,
CacheEntryListenerConfiguration cacheEntryListenerConfiguration) {
CacheConfig cacheConfig = getCacheConfig(name);
if (cacheConfig == null) {
throw new IllegalStateException("CacheConfig does not exist for cache " + name);
}
cacheConfig.removeCacheEntryListenerConfiguration(cacheEntryListenerConfiguration);
}
/**
* Gets the name of the quorum associated with specified cache
*
* @param cacheName name of the cache
* @return name of the associated quorum
* null if there is no associated quorum
*/
@Override
public String getQuorumName(String cacheName) {
if (configs.get(cacheName) == null) {
return null;
}
return configs.get(cacheName).getQuorumName();
}
/**
* Registers and {@link com.hazelcast.cache.impl.client.CacheInvalidationListener} for specified cacheName
.
*
* @param name the name of the cache that {@link com.hazelcast.cache.impl.CacheEventListener} will be registered for
* @param listener the {@link com.hazelcast.cache.impl.CacheEventListener} to be registered for specified cache
* @param localOnly true if only events originated from this member wants be listened, false if all invalidation events in the
* cluster wants to be listened
* @return the id which is unique for current registration
*/
@Override
public String addInvalidationListener(String name, CacheEventListener listener, boolean localOnly) {
EventService eventService = nodeEngine.getEventService();
EventRegistration registration;
if (localOnly) {
registration = eventService.registerLocalListener(SERVICE_NAME, name, listener);
} else {
registration = eventService.registerListener(SERVICE_NAME, name, listener);
}
return registration.getId();
}
/**
* Sends an invalidation event for given cacheName
with specified key
* from mentioned source with sourceUuid
.
*
* @param name the name of the cache that invalidation event is sent for
* @param key the {@link com.hazelcast.nio.serialization.Data} represents the invalidation event
* @param sourceUuid an id that represents the source for invalidation event
*/
@Override
public void sendInvalidationEvent(String name, Data key, String sourceUuid) {
cacheEventHandler.sendInvalidationEvent(name, key, sourceUuid);
}
@Override
public Runnable prepareMergeRunnable() {
return cacheSplitBrainHandler.prepareMergeRunnable();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy