package nl.thanod.cassandra;
import java.io.InvalidClassException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.nio.charset.Charset;
import java.util.*;
import javax.imageio.spi.ServiceRegistry;
import nl.thanod.cassandra.bytes.ByteStringTranslator;
import nl.thanod.cassandra.bytes.ByteTranslator;
import org.apache.cassandra.thrift.*;
import org.apache.cassandra.thrift.Cassandra.Client;
import org.apache.thrift.TException;
public class Store {
public static final Charset STRING_ENCODING = Charset.forName("UTF-8");
public static <T> T ColumnsToObject(Class<T> clazz, Iterable<Column> columns, byte[] key) throws InstantiationException, IllegalAccessException, InvalidClassException, SecurityException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
if (columns == null)
return null;
Constructor<T> con = clazz.getDeclaredConstructor();
if (con == null)
return null;
if (!con.isAccessible())
con.setAccessible(true);
T o = con.newInstance();
// T o = clazz.newInstance();
for (Field f : clazz.getDeclaredFields()) {
if ((f.getModifiers() & Modifier.TRANSIENT) == Modifier.TRANSIENT)
continue;
Column c = getColumnByName(columns, ByteStringTranslator.bytes(f.getName()));
// check if there is a column found for this field
if (c == null)
throw new InvalidClassException("Could not find an entry for the non-transient field " + f.getDeclaringClass().getCanonicalName() + "." + f.getName() + " in the database.");
make(f, o, c.value);
}
Field keyfield = getKeyField(clazz);
if (keyfield != null)
make(keyfield, o, key);
return o;
}
public static String getFieldFullName(Field f) {
return f.getDeclaringClass() + "." + f.getName();
}
public static <T> T load(Client client, String keyspace, String column_family, byte[] key, Class<T> type) {
try {
ColumnParent parent = new ColumnParent(column_family);
SlicePredicate predicate = new SlicePredicate();
predicate.column_names = new LinkedList<byte[]>();
for (Field f : type.getDeclaredFields())
if ((f.getModifiers() & Modifier.TRANSIENT) != Modifier.TRANSIENT)
predicate.column_names.add(ByteStringTranslator.bytes(f.getName()));
List<ColumnOrSuperColumn> loaded = client.get_slice(keyspace, ByteStringTranslator.make(key), parent, predicate, ConsistencyLevel.ONE);
List<Column> columns = new LinkedList<Column>();
for (ColumnOrSuperColumn c : loaded)
columns.add(c.column);
return ColumnsToObject(type, columns, key);
} catch (Throwable ball) {
ball.printStackTrace();
return null;
}
}
public static <T> T load(Client client, String keyspace, String column_fammily, String key, byte[] super_column, Class<T> type) {
Throwable thing = null;
try {
ColumnPath path = new ColumnPath();
path.column_family = column_fammily;
if (super_column != null)
path.super_column = super_column;
ColumnOrSuperColumn corsc = client.get(keyspace, key, path, ConsistencyLevel.ONE);
if (corsc.super_column == null)
throw new InvalidClassException("Could not find a " + type.getCanonicalName() + " at " + keyspace + "." + column_fammily + "['" + key + "'][" + Arrays.toString(super_column) + "]");
return ColumnsToObject(type, corsc.super_column.columns, corsc.super_column.name);
} catch (NotFoundException ball) {
return null;
} catch (Throwable ball) {
thing = ball;
}
throw new RuntimeException("Was not able to load an instance of " + type.getCanonicalName(), thing);
}
public static <T> T load(SuperColumn super_column, Class<T> type) throws InvalidClassException, InstantiationException, IllegalAccessException, SecurityException, IllegalArgumentException, NoSuchMethodException, InvocationTargetException {
return ColumnsToObject(type, super_column.columns, super_column.name);
}
public static List<Column> ObjectToColumn(Object o) {
long time = getCassandraTime();
List<Column> list = new LinkedList<Column>();
Class<?> type = o.getClass();
for (Field f : type.getDeclaredFields()) {
if ((f.getModifiers() & Modifier.TRANSIENT) == Modifier.TRANSIENT)
continue;
if (!f.isAccessible())
f.setAccessible(true);
byte[] val = bytes(o, f);
if (val != null)
list.add(new Column(ByteStringTranslator.bytes(f.getName()), val, time));
}
return list;
}
// public static void store(Cassandra.Client client, String keyspace, String column_family, ConsistencyLevel consistency_level, Object... os) throws InvalidRequestException, UnavailableException, TimedOutException, TException {
// for (Object o : os) {
// Class<?> clazz = o.getClass();
// Field key = getKeyField(clazz);
// List<Column> columns = ObjectToColumn(o);
// if (key == null)
// throw new RuntimeException("No key defined in " + clazz.getCanonicalName());
// if (!key.isAccessible())
// key.setAccessible(true);
// // if (key.getType().equals(String.class))
// // throw new
// // RuntimeException("Due to limits of the current cassandra thrift api the key "
// // + getFieldFullName(key) + " has to be from type " +
// // String.class);
// try {
// Map<String, List<ColumnOrSuperColumn>> cfmap = new TreeMap<String, List<ColumnOrSuperColumn>>();
// String k;
// if (key.getType().equals(String.class))
// k = (String) key.get(o);
// else{
// k = ByteStringTranslator.make(bytes(o, key));
// }
// List<ColumnOrSuperColumn> list = cfmap.get(k);
// if (list == null) {
// list = new LinkedList<ColumnOrSuperColumn>();
// cfmap.put(column_family, list);
// }
// for (Column co : columns) {
// ColumnOrSuperColumn c = new ColumnOrSuperColumn();
// c.column = co;
// list.add(c);
// }
// client.batch_insert(keyspace, k, cfmap, consistency_level);
// } catch (Throwable ball) {
// throw new RuntimeException(ball);
// }
// }
// }
public static void store(Cassandra.Client client, String keyspace, String column_family, String super_column, ConsistencyLevel consistency_level, Object... os) throws InvalidRequestException, UnavailableException, TimedOutException, TException {
Map<String, List<ColumnOrSuperColumn>> cfmap = new TreeMap<String, List<ColumnOrSuperColumn>>();
for (Object o : os) {
Class<?> clazz = o.getClass();
Field key = getKeyField(clazz);
List<Column> columns = ObjectToColumn(o);
if (key == null)
throw new RuntimeException("No key defined in " + clazz.getCanonicalName());
if (!key.isAccessible())
key.setAccessible(true);
ColumnOrSuperColumn c = new ColumnOrSuperColumn();
SuperColumn s = new SuperColumn();
s.columns = columns;
s.name = bytes(o, key);
c.super_column = s;
List<ColumnOrSuperColumn> list = cfmap.get(column_family);
if (list == null) {
list = new LinkedList<ColumnOrSuperColumn>();
cfmap.put(column_family, list);
}
list.add(c);
}
client.batch_insert(keyspace, super_column, cfmap, consistency_level);
}
private static byte[] bytes(Object o, Field f) {
Class<?> type = f.getType();
Iterator<ByteTranslator> list = ServiceRegistry.lookupProviders(ByteTranslator.class);
while (list.hasNext()) {
ByteTranslator trans = list.next();
if (trans.canTranslate(type))
return trans.getBytes(f, o);
}
throw new RuntimeException("Not able to store " + getFieldFullName(f) + " because it is not known how a " + type.getCanonicalName() + " is transformed in a " + byte[].class.getCanonicalName());
}
private static long getCassandraTime() {
return System.currentTimeMillis() * 1000;
}
private static Column getColumnByName(Iterable<Column> columns, byte[] key) {
for (Column c : columns)
if (Arrays.equals(key, c.name))
return c;
return null;
}
private static Field getKeyField(Class<?> clazz) {
Field key = null;
for (Field f : clazz.getDeclaredFields())
if (f.isAnnotationPresent(Key.class))
if (key == null)
key = f;
else
throw new RuntimeException("There are multiple keys defined in " + clazz.getCanonicalName() + ". For example but not limited to " + getFieldFullName(key) + " and " + getFieldFullName(key));
return key;
}
private static void make(Field f, Object o, byte[] bytes) {
Class<?> type = f.getType();
Iterator<ByteTranslator> list = ServiceRegistry.lookupProviders(ByteTranslator.class);
while (list.hasNext()) {
ByteTranslator trans = list.next();
if (trans.canTranslate(type)) {
if (!f.isAccessible())
f.setAccessible(true);
trans.setBytes(f, o, bytes);
return;
}
}
throw new RuntimeException("Not able to restore " + getFieldFullName(f) + " because it is not known how a " + byte[].class.getCanonicalName() + " is transformed in a " + type.getCanonicalName());
}
}