package net.sitemorph.protostore;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Message;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* Preloading crud store which loads all elements into memory on build. Note
* that this is for small data stores as it reads all data.
*
* Note that the preload urn store doesn't respect sort order.
*/
public class PreloadUrnCrudStore<T extends Message> implements CrudStore<T> {
private CrudStore<T> writeStore;
private Map<String, T> urnMap = Maps.newHashMap();
private Set<FieldDescriptor> indexes = Sets.newHashSet();
private FieldDescriptor urnDescriptor;
private PreloadUrnCrudStore() {}
public static class Builder<M extends Message> {
private CrudStore<M> writeStore;
private M.Builder prototype;
private String urnField;
private Set<String> indexes = Sets.newHashSet();
public Builder() {}
public Builder<M> setPrototype(M.Builder prototype) {
this.prototype = prototype;
return this;
}
public Builder<M> setUrnField(String urnField) {
this.urnField = urnField;
return this;
}
public Builder<M> addIndexField(String name) {
indexes.add(name);
return this;
}
public Builder<M> setWriteStore(CrudStore<M> writeStore) {
this.writeStore = writeStore;
return this;
}
public CrudStore<M> build() throws CrudException {
PreloadUrnCrudStore<M> result = new PreloadUrnCrudStore<M>();
result.writeStore = writeStore;
for (FieldDescriptor descriptor : prototype.getDescriptorForType().getFields()) {
if (descriptor.getName().equals(urnField)) {
result.urnDescriptor = descriptor;
} else if (indexes.contains(descriptor.getName())) {
result.indexes.add(descriptor);
}
}
if (null == result.urnDescriptor) {
throw new CrudException("Could not locate urn field: " + urnField);
}
CrudIterator<M> priors = writeStore.read(prototype);
while (priors.hasNext()) {
M prior = priors.next();
String urn = String.valueOf(prior.getField(result.urnDescriptor));
result.urnMap.put(urn, prior);
}
priors.close();
return result;
}
}
@Override
public T create(Message.Builder builder) throws CrudException {
T result = writeStore.create(builder);
String urn = String.valueOf(result.getField(urnDescriptor));
urnMap.put(urn, result);
return result;
}
@Override
public CrudIterator<T> read(Message.Builder builder) throws CrudException {
// urn first
if (builder.hasField(urnDescriptor)) {
String urn = String.valueOf(builder.getField(urnDescriptor));
if (!urnMap.containsKey(urn)) {
throw new MessageNotFoundException("Could not find urn: " + urn);
}
List<T> singleton = Lists.newArrayList();
singleton.add(urnMap.get(urn));
return new AllDataIterator<T>(singleton);
}
// iterate over the index fields, if one set return that
for (FieldDescriptor index : indexes) {
if (builder.hasField(index)) {
List<T> result = Lists.newArrayList();
for (Entry<String, T> entry : urnMap.entrySet()) {
if (builder.getField(index).equals(entry.getValue().getField(index))) {
result.add(entry.getValue());
}
}
return new AllDataIterator<T>(result);
}
}
// return all data
List<T> result = Lists.newArrayList();
result.addAll(urnMap.values());
return new AllDataIterator<T>(result);
}
@Override
public T update(Message.Builder builder) throws CrudException {
T updated = writeStore.update(builder);
String urn = String.valueOf(updated.getField(urnDescriptor));
urnMap.put(urn, updated);
return updated;
}
@Override
public void delete(T message) throws CrudException {
String urn = String.valueOf(message.getField(urnDescriptor));
urnMap.remove(urn);
writeStore.delete(message);
}
@Override
public void close() throws CrudException {
urnMap.clear();
urnMap = null;
writeStore.close();
}
}