package org.radargun.service;
import java.util.concurrent.ExecutionException;
import net.spy.memcached.CASResponse;
import net.spy.memcached.CASValue;
import net.spy.memcached.MemcachedClient;
import org.radargun.traits.BasicOperations;
import org.radargun.traits.ConditionalOperations;
/**
* Implementation of {@link BasicOperations} and {@link ConditionalOperations}
* through the Memcached protocol, using SpyMemcached implementation.
* The memcached CAS operation is used to implement some of the operations,
* therefore, some operations may require multiple actual calls.
*
* @author Radim Vansa <rvansa@redhat.com>
*/
public class SpyMemcachedOperations implements BasicOperations, ConditionalOperations {
private final SpyMemcachedService service;
public SpyMemcachedOperations(SpyMemcachedService service) {
this.service = service;
}
@Override
public <K, V> SpyMemcachedCache<K, V> getCache(String cacheName) {
if (cacheName != null && (service.cacheName == null || !service.cacheName.equals(cacheName))) {
throw new UnsupportedOperationException();
}
return new SpyMemcachedCache();
}
protected class SpyMemcachedCache<K, V> implements BasicOperations.Cache<K, V>, ConditionalOperations.Cache<K, V> {
private final MemcachedClient client;
public SpyMemcachedCache() {
client = service.nextClient();
}
@Override
public V get(K key) {
return (V) client.get(key.toString());
}
@Override
public boolean containsKey(K key) {
return client.get(key.toString()) != null;
}
@Override
public void put(K key, V value) {
try {
if (!client.set(key.toString(), 0, value).get()) {
throw new IllegalStateException("PUT failed");
}
} catch (InterruptedException e) {
throw new IllegalStateException(e);
} catch (ExecutionException e) {
throw new IllegalStateException(e);
}
}
@Override
public V getAndPut(K key, V value) {
String stringKey = key.toString();
CASResponse response;
for (; ; ) {
CASValue<Object> prev = client.gets(stringKey);
if (prev.getValue() == null) {
try {
response = client.add(stringKey, 0, value).get() ? CASResponse.OK : CASResponse.EXISTS;
} catch (InterruptedException e) {
throw new IllegalStateException(e);
} catch (ExecutionException e) {
throw new IllegalStateException(e);
}
} else {
response = client.cas(stringKey, prev.getCas(), value);
}
if (response == CASResponse.OK) {
return (V) prev.getValue();
}
}
}
@Override
public boolean remove(K key) {
try {
return client.delete(key.toString()).get();
} catch (InterruptedException e) {
throw new IllegalStateException(e);
} catch (ExecutionException e) {
throw new IllegalStateException(e);
}
}
@Override
public V getAndRemove(K key) {
String stringKey = key.toString();
for (; ; ) {
CASValue<Object> prev = client.gets(stringKey);
if (prev.getValue() == null) {
return null;
} else {
if (client.cas(stringKey, prev.getCas(), null) == CASResponse.OK) {
return (V) prev.getValue();
}
}
}
}
@Override
public void clear() {
try {
client.flush().get();
} catch (InterruptedException e) {
throw new IllegalStateException(e);
} catch (ExecutionException e) {
throw new IllegalStateException(e);
}
}
@Override
public boolean putIfAbsent(K key, V value) {
try {
return client.add(key.toString(), 0, value).get();
} catch (InterruptedException e) {
throw new IllegalStateException(e);
} catch (ExecutionException e) {
throw new IllegalStateException(e);
}
}
@Override
public boolean remove(K key, V oldValue) {
String stringKey = key.toString();
for (; ; ) {
CASValue<Object> prev = client.gets(stringKey);
if (oldValue.equals(prev.getValue())) {
if (client.cas(stringKey, prev.getCas(), null) == CASResponse.OK) {
return true;
}
} else {
return false;
}
}
}
@Override
public boolean replace(K key, V oldValue, V newValue) {
String stringKey = key.toString();
for (; ; ) {
CASValue<Object> prev = client.gets(stringKey);
if (oldValue.equals(prev.getValue())) {
if (client.cas(stringKey, prev.getCas(), newValue) == CASResponse.OK) {
return true;
}
} else {
return false;
}
}
}
@Override
public boolean replace(K key, V value) {
try {
return client.replace(key.toString(), 0, value).get();
} catch (InterruptedException e) {
throw new IllegalStateException(e);
} catch (ExecutionException e) {
throw new IllegalStateException(e);
}
}
@Override
public V getAndReplace(K key, V value) {
String stringKey = key.toString();
CASResponse response;
for (; ; ) {
CASValue<Object> prev = client.gets(stringKey);
if (prev.getValue() == null) {
return null;
} else {
response = client.cas(stringKey, prev.getCas(), value);
}
if (response == CASResponse.OK) {
return (V) prev.getValue();
}
}
}
}
}