com.hazelcast.cache.impl.CacheEntryProcessorEntry Maven / Gradle / Ivy
/*
* 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;
import com.hazelcast.cache.impl.record.CacheRecord;
import com.hazelcast.nio.serialization.Data;
import com.hazelcast.spi.impl.operationexecutor.impl.PartitionOperationThread;
import javax.cache.configuration.Factory;
import javax.cache.expiry.ExpiryPolicy;
import javax.cache.processor.MutableEntry;
import static com.hazelcast.util.Preconditions.checkNotNull;
/**
* This class is an implementation of {@link MutableEntry} which is provided into
* {@link javax.cache.processor.EntryProcessor#process(javax.cache.processor.MutableEntry, Object...)}.
* CacheEntryProcessorEntry may face multiple mutating operations like setValue, remove or CacheLoading, etc.
* This implementation may handle multiple operations executed on this entry and persist the resultant state into
* {@link CacheRecordStore} after entry processor get completed.
*
* @param the type of key.
* @param the type of value.
* @see javax.cache.processor.EntryProcessor#process(javax.cache.processor.MutableEntry, Object...)
*/
public class CacheEntryProcessorEntry
implements MutableEntry {
protected K key;
protected V value;
protected State state = State.NONE;
protected final Data keyData;
protected R record;
protected R recordLoaded;
protected V valueLoaded;
protected final AbstractCacheRecordStore cacheRecordStore;
protected final long now;
protected final long start;
protected final ExpiryPolicy expiryPolicy;
protected final int completionId;
public CacheEntryProcessorEntry(Data keyData, R record,
AbstractCacheRecordStore cacheRecordStore,
long now, int completionId) {
this.keyData = keyData;
this.record = record;
this.cacheRecordStore = cacheRecordStore;
this.now = now;
this.completionId = completionId;
this.start = cacheRecordStore.cacheConfig.isStatisticsEnabled() ? System.nanoTime() : 0;
final Factory expiryPolicyFactory =
cacheRecordStore.cacheConfig.getExpiryPolicyFactory();
this.expiryPolicy = expiryPolicyFactory.create();
}
@Override
public boolean exists() {
return (record != null && state == State.NONE) || this.value != null;
}
@Override
public void remove() {
this.value = null;
this.state = (this.state == State.CREATE || this.state == State.LOAD) ? State.NONE : State.REMOVE;
}
@Override
public void setValue(V value) {
checkNotNull(value, "Null value not allowed");
if (this.record == null) {
this.state = State.CREATE;
} else {
this.state = State.UPDATE;
}
this.value = value;
}
@Override
public K getKey() {
if (key == null) {
key = (K) cacheRecordStore.cacheService.toObject(keyData);
}
return key;
}
@Override
public V getValue() {
if (state == State.REMOVE) {
return null;
}
if (value != null) {
return value;
}
if (record != null) {
state = State.ACCESS;
value = getRecordValue(record);
return value;
}
if (recordLoaded == null) {
//LOAD IT
recordLoaded = (R) cacheRecordStore.readThroughRecord(keyData, now);
}
if (recordLoaded != null) {
state = State.LOAD;
valueLoaded = getRecordValue(recordLoaded);
return valueLoaded;
}
return null;
}
protected V getRecordValue(R record) {
final Object objValue;
switch (cacheRecordStore.cacheConfig.getInMemoryFormat()) {
case BINARY:
objValue = cacheRecordStore.cacheService.toObject(record.getValue());
break;
case OBJECT:
objValue = record.getValue();
break;
default:
throw new IllegalArgumentException("Invalid storage format: "
+ cacheRecordStore.cacheConfig.getInMemoryFormat());
}
return (V) objValue;
}
public R getRecord() {
assert (Thread.currentThread() instanceof PartitionOperationThread);
return record;
}
/**
* Provides a similar functionality as committing a transaction. So, at the end of the process method, applyChanges
* will be called to apply latest data into {@link CacheRecordStore}.
*/
public void applyChanges() {
final boolean isStatisticsEnabled = cacheRecordStore.cacheConfig.isStatisticsEnabled();
final CacheStatisticsImpl statistics = cacheRecordStore.statistics;
switch (state) {
case ACCESS:
cacheRecordStore.accessRecord(keyData, record, expiryPolicy, now);
break;
case CREATE:
if (isStatisticsEnabled) {
statistics.increaseCachePuts(1);
statistics.addGetTimeNanos(System.nanoTime() - start);
}
boolean saved =
cacheRecordStore.createRecordWithExpiry(keyData, value, expiryPolicy,
now, false, completionId) != null;
onCreate(keyData, value, expiryPolicy, now, false, completionId, saved);
break;
case LOAD:
saved = cacheRecordStore.createRecordWithExpiry(keyData, valueLoaded, expiryPolicy,
now, true, completionId) != null;
onLoad(keyData, valueLoaded, expiryPolicy, now, true, completionId, saved);
break;
case UPDATE:
saved = cacheRecordStore.updateRecordWithExpiry(keyData, value, record,
expiryPolicy, now, false, completionId);
onUpdate(keyData, value, record, expiryPolicy, now, false, completionId, saved);
if (isStatisticsEnabled) {
statistics.increaseCachePuts(1);
statistics.addGetTimeNanos(System.nanoTime() - start);
}
break;
case REMOVE:
boolean removed = cacheRecordStore.remove(keyData, null, completionId);
onRemove(keyData, null, completionId, removed);
break;
case NONE:
cacheRecordStore.publishEvent(
CacheEventContextUtil.createCacheCompleteEvent(
cacheRecordStore.toEventData(keyData),
completionId));
break;
default:
break;
}
}
protected void onCreate(Data key, Object value, ExpiryPolicy expiryPolicy,
long now, boolean disableWriteThrough, int completionId, boolean saved) {
}
protected void onLoad(Data key, Object value, ExpiryPolicy expiryPolicy,
long now, boolean disableWriteThrough, int completionId, boolean saved) {
}
protected void onUpdate(Data key, Object value, R record, ExpiryPolicy expiryPolicy,
long now, boolean disableWriteThrough, int completionId, boolean saved) {
}
protected void onRemove(Data key, String source, int completionId, boolean removed) {
}
@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");
}
protected enum State {
NONE, ACCESS, UPDATE, LOAD, CREATE, REMOVE
}
}