package com.rubiconproject.oss.kv.test;
import java.io.IOException;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import javax.management.MBeanInfo;
import javax.management.MBeanServer;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import junit.framework.TestCase;
import com.rubiconproject.oss.kv.KeyValueStore;
import com.rubiconproject.oss.kv.KeyValueStoreStatus;
import com.rubiconproject.oss.kv.KeyValueStoreUnavailable;
import com.rubiconproject.oss.kv.ManagedKeyValueStore;
import com.rubiconproject.oss.kv.backends.IterableKeyValueStore;
import com.rubiconproject.oss.kv.backends.KeyValueStoreIterator;
import com.rubiconproject.oss.kv.mgmt.JMXMbeanServerFactory;
import com.rubiconproject.oss.kv.transcoder.ByteArrayTranscoder;
import com.rubiconproject.oss.kv.transcoder.SerializableTranscoder;
import com.rubiconproject.oss.kv.transcoder.Transcoder;
import com.rubiconproject.oss.kv.tx.KeyValueStoreTransaction;
import com.rubiconproject.oss.kv.tx.TransactionalKeyValueStore;
public abstract class KeyValueStoreBackendTestCase extends TestCase {
public abstract void testBackend() throws Exception;
protected void doTestBackend(KeyValueStore store) throws Exception {
String key = "some.key";
String objectKey = "some.object.key";
SerializableTranscoder serializer = new SerializableTranscoder();
assertEquals(store.getStatus(), KeyValueStoreStatus.Offline);
try {
store.get(key);
throw new Exception("Should not be available");
} catch (KeyValueStoreUnavailable expected) {
}
store.start();
assertEquals(store.getStatus(), KeyValueStoreStatus.Online);
store.delete(key);
assertNull(store.get(key));
assertFalse(store.exists(key));
// test with a string, using object transcoder
store.set(key, "hello world");
assertTrue(store.exists(key));
assertEquals(store.get(key), "hello world");
// test with a string, using UTF-8 transcoder
Transcoder t = new Transcoder() {
public Object decode(byte[] bytes) throws IOException {
return new String(bytes, "UTF-8");
}
public byte[] encode(Object value) throws IOException {
return ((String) value).getBytes("UTF-8");
}
};
store.set(key, "hello dude", t);
assertTrue(store.exists(key));
assertEquals(store.get(key, t), "hello dude");
SampleV v = new SampleV(10, "hello world", 12);
store.set(objectKey, v, serializer);
Thread.sleep(100l);
assertTrue(store.exists(objectKey));
SampleV v2 = (SampleV) store.get(objectKey, serializer);
assertNotNull(v2);
assertEquals(v2.someRequiredInt, v.someRequiredInt);
assertEquals(v2.someString, v.someString);
assertEquals(v2.someOptionalDouble, v.someOptionalDouble);
// test getBulk()
Map<String, Object> map = store.getBulk("xyz123", "abcdefg",
"sdfdsfer", "weruiwer");
assertEquals(map.size(), 0);
map = store.getBulk(Arrays.asList(new String[] { objectKey, "abcdefg" }), serializer);
assertEquals(map.size(), 1);
assertEquals(((SampleV) map.get(objectKey)).someRequiredInt,
v.someRequiredInt);
map = store.getBulk(Arrays
.asList(new String[] { "sxyzxv", "123", objectKey }), serializer);
assertEquals(map.size(), 1);
assertEquals(((SampleV) map.get(objectKey)).someRequiredInt,
v.someRequiredInt);
map = store.getBulk(Arrays
.asList(new String[] { "12345", objectKey, "sfdsdf" }),
new ByteArrayTranscoder());
assertEquals(map.size(), 1);
// test iterator if applicable
if (store instanceof IterableKeyValueStore)
doTestIterator((IterableKeyValueStore) store);
// set status to read only
store.setStatus(KeyValueStoreStatus.ReadOnly);
SampleV v3 = (SampleV) store.get(objectKey, serializer);
assertNotNull(v3);
try {
store.set(key, v3);
throw new Exception("Status should be readonly");
} catch (KeyValueStoreUnavailable e) {
}
store.setStatus(KeyValueStoreStatus.Online);
// test start/stop
store.stop();
try {
store.set(key, v3);
throw new Exception("Status should be readonly");
} catch (KeyValueStoreUnavailable e) {
}
store.start();
store.delete(key);
Thread.sleep(100l);
assertNull(store.get(key));
doTestJMX(store);
}
private void doTestIterator(IterableKeyValueStore store) throws Exception {
int keyCount = 0;
KeyValueStoreIterator storeIterator = store.iterkeys();
Iterator<String> iter = storeIterator.iterator();
while (iter.hasNext()) {
String s = iter.next();
++keyCount;
}
assertTrue(keyCount > 0);
storeIterator.close();
}
private void doTestTransactions(TransactionalKeyValueStore store)
throws Exception {
String key = "test.tx.key";
SampleV v = new SampleV(10, "hello world", 12.2d);
Transcoder transcoder = new SerializableTranscoder();
KeyValueStoreTransaction<SampleV> tx = store.txGet(key, transcoder);
assertFalse(store.exists(key));
assertNull(tx.getObject());
tx.setObject(v);
store.txSet(tx, key, transcoder);
assertTrue(store.exists(key));
// start two transactions at once
tx = store.txGet(key, transcoder);
KeyValueStoreTransaction<SampleV> tx2 = store.txGet(key, transcoder);
// attempt to save the second
tx2.getObject().someRequiredInt = 13;
store.txSet(tx2, key);
}
private void doTestJMX(KeyValueStore store) throws Exception {
if (!(store instanceof ManagedKeyValueStore))
return;
ManagedKeyValueStore managedStore = (ManagedKeyValueStore) store;
ObjectName objectName = new ObjectName(managedStore
.getMXBeanObjectName());
MBeanServer mbeanServer = JMXMbeanServerFactory.getMBeanServer();
ObjectInstance instance = mbeanServer.getObjectInstance(objectName);
assertNotNull(instance);
MBeanInfo info = mbeanServer.getMBeanInfo(objectName);
assertNotNull(info);
mbeanServer
.invoke(objectName, "stop", new Object[] {}, new String[] {});
assertEquals(store.getStatus(), KeyValueStoreStatus.Offline);
mbeanServer.invoke(objectName, "start", new Object[] {},
new String[] {});
assertEquals(store.getStatus(), KeyValueStoreStatus.Online);
}
public static class SampleV implements Serializable, Comparable<SampleV> {
private static final long serialVersionUID = 7278340350600213753L;
public int someRequiredInt;
public String someString;
public double someOptionalDouble;
public SampleV() {
}
public SampleV(int someRequiredInt, String someString,
double someOptionalDouble) {
this.someRequiredInt = someRequiredInt;
this.someString = someString;
this.someOptionalDouble = someOptionalDouble;
}
public boolean equals(SampleV v) {
return someRequiredInt == v.someRequiredInt
&& someString.equals(v.someString)
&& someOptionalDouble == v.someOptionalDouble;
}
public int compareTo(SampleV v) {
int result = new Integer(someRequiredInt).compareTo(new Integer(v.someRequiredInt));
result = (result == 0) ? (someString.compareTo(v.someString)) : result;
result = (result == 0) ? (new Double(someOptionalDouble).compareTo(new Double(v.someOptionalDouble))) : result;
return result;
}
}
}