package org.sdnplatform.sync.internal.remote;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.sdnplatform.sync.IClosableIterator;
import org.sdnplatform.sync.IVersion;
import org.sdnplatform.sync.Versioned;
import org.sdnplatform.sync.error.RemoteStoreException;
import org.sdnplatform.sync.error.SyncException;
import org.sdnplatform.sync.error.SyncRuntimeException;
import org.sdnplatform.sync.internal.rpc.TProtocolUtil;
import org.sdnplatform.sync.internal.store.IStore;
import org.sdnplatform.sync.internal.store.StoreUtils;
import org.sdnplatform.sync.internal.util.ByteArray;
import org.sdnplatform.sync.internal.util.Pair;
import org.sdnplatform.sync.thrift.AsyncMessageHeader;
import org.sdnplatform.sync.thrift.SyncMessage;
import org.sdnplatform.sync.thrift.CursorRequestMessage;
import org.sdnplatform.sync.thrift.GetRequestMessage;
import org.sdnplatform.sync.thrift.KeyedValues;
import org.sdnplatform.sync.thrift.MessageType;
import org.sdnplatform.sync.thrift.PutRequestMessage;
/**
* A store implementation that will connect to a remote sync instance
* @author readams
*/
public class RemoteStore implements IStore<ByteArray, byte[]> {
private String storeName;
private RemoteSyncManager syncManager;
public RemoteStore(String storeName, RemoteSyncManager syncManager) {
super();
this.storeName = storeName;
this.syncManager = syncManager;
}
// *************************
// IStore<ByteArray, byte[]>
// *************************
@Override
public List<Versioned<byte[]>> get(ByteArray key) throws SyncException {
StoreUtils.assertValidKey(key);
GetRequestMessage grm = new GetRequestMessage();
AsyncMessageHeader header = new AsyncMessageHeader();
header.setTransactionId(syncManager.getTransactionId());
grm.setHeader(header);
grm.setKey(key.get());
grm.setStoreName(storeName);
SyncMessage bsm = new SyncMessage(MessageType.GET_REQUEST);
bsm.setGetRequest(grm);
SyncReply reply = getReply(header.getTransactionId(), bsm);
return reply.getValues();
}
@Override
public IClosableIterator<Entry<ByteArray, List<Versioned<byte[]>>>>
entries() {
return new RemoteIterator();
}
@Override
public void put(ByteArray key, Versioned<byte[]> value)
throws SyncException {
StoreUtils.assertValidKey(key);
PutRequestMessage prm = new PutRequestMessage();
AsyncMessageHeader header = new AsyncMessageHeader();
header.setTransactionId(syncManager.getTransactionId());
prm.setHeader(header);
prm.setVersionedValue(TProtocolUtil.getTVersionedValue(value));
prm.setKey(key.get());
prm.setStoreName(storeName);
SyncMessage bsm = new SyncMessage(MessageType.PUT_REQUEST);
bsm.setPutRequest(prm);
getReply(header.getTransactionId(), bsm);
}
@Override
public List<IVersion> getVersions(ByteArray key) throws SyncException {
List<Versioned<byte[]>> values = get(key);
ArrayList<IVersion> versions = new ArrayList<IVersion>();
for (Versioned<byte[]> v : values) {
versions.add(v.getVersion());
}
return versions;
}
@Override
public String getName() {
return storeName;
}
@Override
public void close() throws SyncException {
}
// *************
// Local methods
// *************
private SyncReply getReply(int xid,
SyncMessage bsm)
throws SyncException {
SyncReply reply = null;
try {
Future<SyncReply> future =
syncManager.sendRequest(xid, bsm);
reply = future.get(5, TimeUnit.SECONDS);
} catch (TimeoutException e) {
throw new RemoteStoreException("Timed out on operation", e);
} catch (Exception e) {
throw new RemoteStoreException("Error while waiting for reply", e);
}
if (reply.getError() != null)
throw reply.getError();
return reply;
}
private class RemoteIterator
implements IClosableIterator<Entry<ByteArray,
List<Versioned<byte[]>>>> {
private final int cursorId;
Iterator<KeyedValues> currentChunk;
public RemoteIterator() {
CursorRequestMessage crm = getCRM();
crm.setStoreName(storeName);
SyncMessage bsm = new SyncMessage(MessageType.CURSOR_REQUEST);
bsm.setCursorRequest(crm);
SyncReply reply;
try {
reply = getReply(crm.getHeader().getTransactionId(),
bsm);
} catch (SyncException e) {
throw new SyncRuntimeException(e);
}
this.cursorId = reply.getIntValue();
if (reply.getKeyedValues() != null)
currentChunk = reply.getKeyedValues().iterator();
}
@Override
public boolean hasNext() {
if (currentChunk != null) {
if (currentChunk.hasNext())
return true;
}
Iterator<KeyedValues> nextChunk = getChunk();
if (nextChunk != null) {
currentChunk = nextChunk;
return nextChunk.hasNext();
}
return false;
}
@Override
public Entry<ByteArray, List<Versioned<byte[]>>> next() {
if (!hasNext()) throw new NoSuchElementException();
KeyedValues kv = currentChunk.next();
ByteArray k = new ByteArray(kv.getKey());
List<Versioned<byte[]>> v =
TProtocolUtil.getVersionedList(kv.getValues());
return new Pair<ByteArray, List<Versioned<byte[]>>>(k, v);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public void close() {
CursorRequestMessage crm = getCRM();
crm.setCursorId(cursorId);
crm.setClose(true);
SyncMessage bsm = new SyncMessage(MessageType.CURSOR_REQUEST);
bsm.setCursorRequest(crm);
try {
getReply(crm.getHeader().getTransactionId(),
bsm);
} catch (SyncException e) {
throw new SyncRuntimeException(e);
}
}
private Iterator<KeyedValues> getChunk() {
CursorRequestMessage crm = getCRM();
crm.setCursorId(cursorId);
SyncMessage bsm = new SyncMessage(MessageType.CURSOR_REQUEST);
bsm.setCursorRequest(crm);
SyncReply reply;
try {
reply = getReply(crm.getHeader().getTransactionId(),
bsm);
} catch (SyncException e) {
throw new SyncRuntimeException(e);
}
if (reply.getKeyedValues() == null ||
reply.getKeyedValues().size() == 0) return null;
return reply.getKeyedValues().iterator();
}
private CursorRequestMessage getCRM() {
CursorRequestMessage crm = new CursorRequestMessage();
AsyncMessageHeader header = new AsyncMessageHeader();
header.setTransactionId(syncManager.getTransactionId());
crm.setHeader(header);
return crm;
}
}
}