package com.thinkaurelius.titan.diskstorage.hazelcast;
import java.util.Iterator;
import javax.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.esotericsoftware.minlog.Log;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterators;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.HazelcastInstanceNotActiveException;
import com.hazelcast.core.IMap;
import com.thinkaurelius.titan.diskstorage.StaticBuffer;
import com.thinkaurelius.titan.diskstorage.StorageException;
import com.thinkaurelius.titan.diskstorage.keycolumnvalue.StoreTransaction;
import com.thinkaurelius.titan.diskstorage.keycolumnvalue.keyvalue.CacheStore;
import com.thinkaurelius.titan.diskstorage.keycolumnvalue.keyvalue.CacheUpdateException;
import com.thinkaurelius.titan.diskstorage.keycolumnvalue.keyvalue.KeySelector;
import com.thinkaurelius.titan.diskstorage.keycolumnvalue.keyvalue.KeyValueEntry;
import com.thinkaurelius.titan.diskstorage.util.RecordIterator;
import com.thinkaurelius.titan.diskstorage.util.StaticArrayBuffer;
import com.thinkaurelius.titan.diskstorage.util.StaticByteBuffer;
public class HazelcastCacheStore implements CacheStore {
private static final String UPDATE_EXCEPTION_FORMAT = "Key: %s, has current value different from %s, can't replace with %s.";
private static final Logger log = LoggerFactory.getLogger(HazelcastCacheStore.class);
private final IMap<byte[], StaticBuffer> cache;
public HazelcastCacheStore(String name, HazelcastInstance manager) {
this.cache = manager.getMap(name);
}
@Override
public void replace(StaticBuffer key, StaticBuffer newValue, StaticBuffer oldValue, StoreTransaction txh) throws CacheUpdateException {
byte[] rawKey = key.as(StaticArrayBuffer.ARRAY_FACTORY);
byte[] rawNewValue = newValue.as(StaticArrayBuffer.ARRAY_FACTORY);
// Hazelcast doesn't replace a value when old value was null
// so we have to look and use putIfAbsent(new) if oldValue == null, otherwise use replace(old, new)
if (oldValue == null) {
if (cache.putIfAbsent(rawKey, newValue) != null)
throw new CacheUpdateException(String.format(UPDATE_EXCEPTION_FORMAT, key, oldValue, newValue));
} else if (!cache.replace(rawKey, oldValue, newValue)) {
throw new CacheUpdateException(String.format(UPDATE_EXCEPTION_FORMAT, key, oldValue, newValue));
}
}
@Override
public void delete(StaticBuffer key, StoreTransaction txh) throws StorageException {
cache.remove(key.as(StaticArrayBuffer.ARRAY_FACTORY));
}
@Override
public StaticBuffer get(StaticBuffer key, StoreTransaction txh) throws StorageException {
StaticBuffer value = cache.get(key.as(StaticArrayBuffer.ARRAY_FACTORY));
if (value == null) return null;
return new StaticArrayBuffer(value.as(StaticBuffer.ARRAY_FACTORY));
}
@Override
public boolean containsKey(StaticBuffer key, StoreTransaction txh) throws StorageException {
return cache.containsKey(key.as(StaticArrayBuffer.ARRAY_FACTORY));
}
@Override
public RecordIterator<KeyValueEntry> getKeys(final KeySelector selector, StoreTransaction txh) throws StorageException {
final Iterator<byte[]> keys = Iterators.filter(cache.keySet().iterator(), new Predicate<byte[]>() {
@Override
public boolean apply(@Nullable byte[] key) {
return selector.include(new StaticArrayBuffer(key)) && !selector.reachedLimit();
}
});
return new RecordIterator<KeyValueEntry>() {
@Override
public boolean hasNext() {
return keys.hasNext();
}
@Override
public KeyValueEntry next() {
byte[] key = keys.next();
return new KeyValueEntry(new StaticArrayBuffer(key), cache.get(key));
}
@Override
public void close() {
// nothing to do
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public void acquireLock(StaticBuffer key, StaticBuffer expectedValue, StoreTransaction txh) throws StorageException {
// not supported
}
@Override
public StaticBuffer[] getLocalKeyPartition() throws StorageException {
throw new UnsupportedOperationException();
}
@Override
public String getName() {
return cache.getName();
}
@Override
public void clearStore() {
try {
cache.clear();
} catch (HazelcastInstanceNotActiveException e) {
log.debug("Hazelcast instance inactive during cache clearing", e);
}
}
@Override
public void close() throws StorageException {
try {
cache.destroy();
} catch (HazelcastInstanceNotActiveException e) {
log.debug("Hazelcast instance inactive during cache deletion", e);
}
}
}