/*
* Copyright (c) 2008-2017, 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.map.impl;
import com.hazelcast.config.Config;
import com.hazelcast.config.MapConfig;
import com.hazelcast.config.WanReplicationRef;
import com.hazelcast.core.IFunction;
import com.hazelcast.core.PartitioningStrategy;
import com.hazelcast.internal.serialization.InternalSerializationService;
import com.hazelcast.map.eviction.MapEvictionPolicy;
import com.hazelcast.map.impl.eviction.EvictionChecker;
import com.hazelcast.map.impl.eviction.Evictor;
import com.hazelcast.map.impl.eviction.EvictorImpl;
import com.hazelcast.map.impl.mapstore.MapStoreContext;
import com.hazelcast.map.impl.nearcache.invalidation.InvalidationListener;
import com.hazelcast.map.impl.query.QueryEntryFactory;
import com.hazelcast.map.impl.record.DataRecordFactory;
import com.hazelcast.map.impl.record.ObjectRecordFactory;
import com.hazelcast.map.impl.record.RecordFactory;
import com.hazelcast.map.merge.MapMergePolicy;
import com.hazelcast.nio.ClassLoaderUtil;
import com.hazelcast.nio.serialization.Data;
import com.hazelcast.nio.serialization.SerializableByConvention;
import com.hazelcast.query.impl.Indexes;
import com.hazelcast.query.impl.QueryableEntry;
import com.hazelcast.query.impl.getters.Extractors;
import com.hazelcast.spi.NodeEngine;
import com.hazelcast.spi.partition.IPartitionService;
import com.hazelcast.spi.serialization.SerializationService;
import com.hazelcast.util.ConstructorFunction;
import com.hazelcast.util.ExceptionUtil;
import com.hazelcast.util.MemoryInfoAccessor;
import com.hazelcast.util.RuntimeMemoryInfoAccessor;
import com.hazelcast.wan.WanReplicationPublisher;
import com.hazelcast.wan.WanReplicationService;
import java.util.concurrent.atomic.AtomicInteger;
import static com.hazelcast.config.InMemoryFormat.OBJECT;
import static com.hazelcast.map.impl.eviction.Evictor.NULL_EVICTOR;
import static com.hazelcast.map.impl.mapstore.MapStoreContextFactory.createMapStoreContext;
import static java.lang.System.getProperty;
/**
* Map container for a map with a specific name. Contains config and supporting structures for
* all of the maps' functionalities.
*/
public class MapContainer {
protected final String name;
protected final String quorumName;
protected final MapServiceContext mapServiceContext;
protected final Indexes indexes;
protected final Extractors extractors;
protected final PartitioningStrategy partitioningStrategy;
protected final MapStoreContext mapStoreContext;
protected final SerializationService serializationService;
protected final QueryEntryFactory queryEntryFactory;
protected final InterceptorRegistry interceptorRegistry = new InterceptorRegistry();
protected final IFunction<Object, Data> toDataFunction = new ObjectToData();
protected final ConstructorFunction<Void, RecordFactory> recordFactoryConstructor;
/**
* Holds number of registered {@link InvalidationListener} from clients.
*/
protected final AtomicInteger invalidationListenerCount = new AtomicInteger();
protected WanReplicationPublisher wanReplicationPublisher;
protected MapMergePolicy wanMergePolicy;
protected volatile Evictor evictor;
protected volatile MapConfig mapConfig;
/**
* Operations which are done in this constructor should obey the rules defined
* in the method comment {@link com.hazelcast.spi.PostJoinAwareService#getPostJoinOperation()}
* Otherwise undesired situations, like deadlocks, may appear.
*/
public MapContainer(final String name, final Config config, final MapServiceContext mapServiceContext) {
this.name = name;
this.mapConfig = config.findMapConfig(name);
this.mapServiceContext = mapServiceContext;
NodeEngine nodeEngine = mapServiceContext.getNodeEngine();
this.partitioningStrategy = createPartitioningStrategy();
this.quorumName = mapConfig.getQuorumName();
this.serializationService = nodeEngine.getSerializationService();
this.recordFactoryConstructor = createRecordFactoryConstructor(serializationService);
this.queryEntryFactory = new QueryEntryFactory(mapConfig.getCacheDeserializedValues());
initWanReplication(nodeEngine);
this.extractors = new Extractors(mapConfig.getMapAttributeConfigs(), config.getClassLoader());
this.indexes = new Indexes((InternalSerializationService) serializationService, extractors);
this.mapStoreContext = createMapStoreContext(this);
this.mapStoreContext.start();
initEvictor();
}
// this method is overridden.
public void initEvictor() {
MapEvictionPolicy mapEvictionPolicy = mapConfig.getMapEvictionPolicy();
if (mapEvictionPolicy == null) {
evictor = NULL_EVICTOR;
} else {
MemoryInfoAccessor memoryInfoAccessor = getMemoryInfoAccessor();
EvictionChecker evictionChecker = new EvictionChecker(memoryInfoAccessor, mapServiceContext);
IPartitionService partitionService = mapServiceContext.getNodeEngine().getPartitionService();
evictor = new EvictorImpl(mapEvictionPolicy, evictionChecker, partitionService);
}
}
protected static MemoryInfoAccessor getMemoryInfoAccessor() {
MemoryInfoAccessor pluggedMemoryInfoAccessor = getPluggedMemoryInfoAccessor();
return pluggedMemoryInfoAccessor != null ? pluggedMemoryInfoAccessor : new RuntimeMemoryInfoAccessor();
}
private static MemoryInfoAccessor getPluggedMemoryInfoAccessor() {
String memoryInfoAccessorImpl = getProperty("hazelcast.memory.info.accessor.impl");
if (memoryInfoAccessorImpl == null) {
return null;
}
try {
return ClassLoaderUtil.newInstance(null, memoryInfoAccessorImpl);
} catch (Exception e) {
throw ExceptionUtil.rethrow(e);
}
}
// overridden in different context.
ConstructorFunction<Void, RecordFactory> createRecordFactoryConstructor(final SerializationService serializationService) {
return new ConstructorFunction<Void, RecordFactory>() {
@Override
public RecordFactory createNew(Void notUsedArg) {
switch (mapConfig.getInMemoryFormat()) {
case BINARY:
return new DataRecordFactory(mapConfig, serializationService, partitioningStrategy);
case OBJECT:
return new ObjectRecordFactory(mapConfig, serializationService);
default:
throw new IllegalArgumentException("Invalid storage format: " + mapConfig.getInMemoryFormat());
}
}
};
}
public void initWanReplication(NodeEngine nodeEngine) {
WanReplicationRef wanReplicationRef = mapConfig.getWanReplicationRef();
if (wanReplicationRef == null) {
return;
}
String wanReplicationRefName = wanReplicationRef.getName();
WanReplicationService wanReplicationService = nodeEngine.getWanReplicationService();
wanReplicationPublisher = wanReplicationService.getWanReplicationPublisher(wanReplicationRefName);
wanMergePolicy = mapServiceContext.getMergePolicyProvider().getMergePolicy(wanReplicationRef.getMergePolicy());
}
private PartitioningStrategy createPartitioningStrategy() {
return mapServiceContext.getPartitioningStrategy(mapConfig.getName(), mapConfig.getPartitioningStrategyConfig());
}
public Indexes getIndexes() {
return indexes;
}
public WanReplicationPublisher getWanReplicationPublisher() {
return wanReplicationPublisher;
}
public MapMergePolicy getWanMergePolicy() {
return wanMergePolicy;
}
public boolean isWanReplicationEnabled() {
if (wanReplicationPublisher == null || wanMergePolicy == null) {
return false;
}
return true;
}
public void checkWanReplicationQueues() {
if (isWanReplicationEnabled()) {
wanReplicationPublisher.checkWanReplicationQueues();
}
}
public int getTotalBackupCount() {
return getBackupCount() + getAsyncBackupCount();
}
public int getBackupCount() {
return mapConfig.getBackupCount();
}
public int getAsyncBackupCount() {
return mapConfig.getAsyncBackupCount();
}
public PartitioningStrategy getPartitioningStrategy() {
return partitioningStrategy;
}
public MapServiceContext getMapServiceContext() {
return mapServiceContext;
}
public MapStoreContext getMapStoreContext() {
return mapStoreContext;
}
public MapConfig getMapConfig() {
return mapConfig;
}
public void setMapConfig(MapConfig mapConfig) {
this.mapConfig = mapConfig;
}
public String getName() {
return name;
}
public String getQuorumName() {
return quorumName;
}
public IFunction<Object, Data> toData() {
return toDataFunction;
}
public ConstructorFunction<Void, RecordFactory> getRecordFactoryConstructor() {
return recordFactoryConstructor;
}
public QueryableEntry newQueryEntry(Data key, Object value) {
return queryEntryFactory.newEntry((InternalSerializationService) serializationService, key, value, extractors);
}
public Evictor getEvictor() {
return evictor;
}
// only used for testing purposes
public void setEvictor(Evictor evictor) {
this.evictor = evictor;
}
public Extractors getExtractors() {
return extractors;
}
public boolean hasInvalidationListener() {
return invalidationListenerCount.get() > 0;
}
public void increaseInvalidationListenerCount() {
invalidationListenerCount.incrementAndGet();
}
public void decreaseInvalidationListenerCount() {
invalidationListenerCount.decrementAndGet();
}
public InterceptorRegistry getInterceptorRegistry() {
return interceptorRegistry;
}
// callback called when the MapContainer is de-registered from MapService and destroyed - basically on map-destroy
public void onDestroy() {
}
public boolean shouldCloneOnEntryProcessing() {
return getIndexes().hasIndex() && OBJECT.equals(mapConfig.getInMemoryFormat());
}
@SerializableByConvention
private class ObjectToData implements IFunction<Object, Data> {
@Override
public Data apply(Object input) {
SerializationService ss = mapStoreContext.getSerializationService();
return ss.toData(input, partitioningStrategy);
}
}
}