/* * 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.cache.impl.record; import com.hazelcast.cache.CacheEntryView; import com.hazelcast.cache.impl.CacheContext; import com.hazelcast.cache.impl.CacheEntryIterationResult; import com.hazelcast.cache.impl.CacheKeyIterationResult; import com.hazelcast.internal.eviction.Evictable; import com.hazelcast.internal.eviction.EvictionCandidate; import com.hazelcast.internal.eviction.EvictionListener; import com.hazelcast.nio.serialization.Data; import com.hazelcast.nio.serialization.SerializableByConvention; import com.hazelcast.spi.serialization.SerializationService; import com.hazelcast.util.SampleableConcurrentHashMap; import java.util.AbstractMap; import java.util.ArrayList; import java.util.List; import java.util.Map; @SerializableByConvention public class CacheRecordHashMap extends SampleableConcurrentHashMap<Data, CacheRecord> implements SampleableCacheRecordMap<Data, CacheRecord> { private static final long serialVersionUID = 1L; private final transient SerializationService serializationService; private final transient CacheContext cacheContext; private boolean entryCountingEnable; public CacheRecordHashMap(SerializationService serializationService, int initialCapacity, CacheContext cacheContext) { super(initialCapacity); this.serializationService = serializationService; this.cacheContext = cacheContext; } // Called by only same partition thread. So there is no synchronization and visibility problem. @Override public void setEntryCounting(boolean enable) { if (enable) { if (!entryCountingEnable) { // It was disable before but now it will be enable. // Therefore, we increase the entry count as size of records. cacheContext.increaseEntryCount(size()); } } else { if (entryCountingEnable) { // It was enable before but now it will be disable. // Therefore, we decrease the entry count as size of records. cacheContext.decreaseEntryCount(size()); } } this.entryCountingEnable = enable; } @Override public CacheRecord put(Data key, CacheRecord value) { CacheRecord oldRecord = super.put(key, value); if (oldRecord == null && entryCountingEnable) { // New put cacheContext.increaseEntryCount(); } return oldRecord; } @Override public CacheRecord putIfAbsent(Data key, CacheRecord value) { CacheRecord oldRecord = super.putIfAbsent(key, value); if (oldRecord == null && entryCountingEnable) { // New put cacheContext.increaseEntryCount(); } return oldRecord; } @Override public CacheRecord remove(Object key) { CacheRecord removedRecord = super.remove(key); if (removedRecord != null && entryCountingEnable) { // Removed cacheContext.decreaseEntryCount(); } return removedRecord; } @Override public boolean remove(Object key, Object value) { boolean removed = super.remove(key, value); if (removed && entryCountingEnable) { // Removed cacheContext.decreaseEntryCount(); } return removed; } @Override public void clear() { final int sizeBeforeClear = size(); super.clear(); if (entryCountingEnable) { cacheContext.decreaseEntryCount(sizeBeforeClear); } } private class CacheEvictableSamplingEntry extends SamplingEntry<Data, CacheRecord> implements EvictionCandidate, CacheEntryView { public CacheEvictableSamplingEntry(Data key, CacheRecord value) { super(key, value); } @Override public Object getAccessor() { return key; } @Override public Evictable getEvictable() { return value; } @Override public Object getKey() { return serializationService.toObject(key); } @Override public Object getValue() { return serializationService.toObject(value.getValue()); } @Override public long getCreationTime() { return value.getCreationTime(); } @Override public long getExpirationTime() { return value.getExpirationTime(); } @Override public long getLastAccessTime() { return value.getLastAccessTime(); } @Override public long getAccessHit() { return value.getAccessHit(); } } @Override protected CacheEvictableSamplingEntry createSamplingEntry(Data key, CacheRecord value) { return new CacheEvictableSamplingEntry(key, value); } @Override public CacheKeyIterationResult fetchKeys(int nextTableIndex, int size) { List<Data> keys = new ArrayList<Data>(size); int tableIndex = fetchKeys(nextTableIndex, size, keys); return new CacheKeyIterationResult(keys, tableIndex); } @Override public CacheEntryIterationResult fetchEntries(int nextTableIndex, int size) { List<Map.Entry<Data, CacheRecord>> entries = new ArrayList<Map.Entry<Data, CacheRecord>>(size); int newTableIndex = fetchEntries(nextTableIndex, size, entries); List<Map.Entry<Data, Data>> entriesData = new ArrayList<Map.Entry<Data, Data>>(entries.size()); for (Map.Entry<Data, CacheRecord> entry : entries) { CacheRecord record = entry.getValue(); Data dataValue = serializationService.toData(record.getValue()); entriesData.add(new AbstractMap.SimpleEntry<Data, Data>(entry.getKey(), dataValue)); } return new CacheEntryIterationResult(entriesData, newTableIndex); } @Override public <C extends EvictionCandidate<Data, CacheRecord>> int evict(Iterable<C> evictionCandidates, EvictionListener<Data, CacheRecord> evictionListener) { if (evictionCandidates == null) { return 0; } int actualEvictedCount = 0; for (EvictionCandidate<Data, CacheRecord> evictionCandidate : evictionCandidates) { if (remove(evictionCandidate.getAccessor()) != null) { actualEvictedCount++; if (evictionListener != null) { evictionListener.onEvict(evictionCandidate.getAccessor(), evictionCandidate.getEvictable(), false); } } } return actualEvictedCount; } @Override public Iterable<CacheEvictableSamplingEntry> sample(int sampleCount) { return super.getRandomSamples(sampleCount); } }