package smartkv.client; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.util.Set; import smartkv.server.DataStoreVersion; import smartkv.server.RequestType; import bftsmart.tom.ServiceProxy; import com.google.common.collect.Sets; import com.google.common.primitives.Ints; import com.google.common.primitives.Longs; import com.google.common.primitives.Shorts; /* * TODO - Clean up the messages. Some require sending sizes of values others don't. * * FIXME: Document thread safety. * FIXME: initialize the ServiceProxy. * FIXME: documentation of this class. */ public abstract class AbstractDatastoreProxy implements TableDataStoreProxy{ private static Set<Integer> cids = Sets.newHashSet(); private ServiceProxy server; private static int cid =1; private static ServiceProxy s= new ServiceProxy(1); /** * Initialize this proxy with a given client id. * @param cid The client id used in {@link ServiceProxy} . Remember that is must be globally unique. */ protected AbstractDatastoreProxy(int ignore){ //FIXME ///TODO have a testing setting that verifies if the same cid is being used, and throws an exception to find out where. // server = new ServiceProxy(AbstractDatastoreProxy.cid); AbstractDatastoreProxy.cid += 1; server = s; } /* * @see bonafide.datastore.DatastoreProxy#clear() */ @Override public void clear() { RequestType type = RequestType.CLEAR_DATASTORE; byte[] request = concatArrays(type.byteArrayOrdinal); invokeRequest(type, request); return; } /* * @see bonafide.datastore.DatastoreProxy#createTable(java.lang.String) */ @Override public boolean createTable(String tableName) { RequestType type = RequestType.CREATE_TABLE; byte[] request = concatArrays( type.byteArrayOrdinal, getBytes(tableName)); byte[] result = invokeRequestWithRawReturn(type, request); //Result is null only if table was not created. return result != null; } /* * @see bonafide.datastore.DatastoreProxy#createTable(java.lang.String, int) */ @Override public boolean createTable(String tableName, long maxSize) { RequestType type = RequestType.CREATE_TABLE_MAX_SIZE; byte[] request = concatArrays( type.byteArrayOrdinal, getBytes(tableName), getBytes(maxSize)); byte[] result = invokeRequestWithRawReturn(type, request); //Result is null only if table was not created. return result != null; } @Override public boolean createPointerTable(String tableName, String reference) { RequestType type = RequestType.CREATE_POINTER_TABLE; byte[] request = concatArrays( type.byteArrayOrdinal, getBytes(tableName), getBytes(reference) ); byte[] result = invokeRequestWithRawReturn(type, request); //Result is null only if table was not created. return result != null; } /* * @see bonafide.datastore.DatastoreProxy#removeTable(java.lang.String) */ @Override public boolean removeTable(String tableName) { RequestType type = RequestType.REMOVE_TABLE; byte[] request = concatArrays( type.byteArrayOrdinal, getBytes(tableName)); byte[] result = invokeRequestWithRawReturn(type, request); //Result is null only if table was not removed. return result != null; } /* * @see bonafide.datastore.DatastoreProxy#containsTable(java.lang.String) */ @Override public boolean containsTable(String tableName) { RequestType type = RequestType.CONTAINS_TABLE; byte[] request = concatArrays( type.byteArrayOrdinal, getBytes(tableName)); byte[] result = invokeRequestWithRawReturn(type, request); //Result is null only if table was not removed. return result != null; } /* * @see bonafide.datastore.DatastoreProxy#clear(java.lang.String) */ @Override public void clear(String tableName) { RequestType type = RequestType.CLEAR_TABLE; byte[] request = concatArrays( type.byteArrayOrdinal, getBytes(tableName)); invokeRequest(type, request); } /* * @see bonafide.datastore.DatastoreProxy#isEmpty(java.lang.String) */ @Override public boolean isEmpty(String tableName) { RequestType type = RequestType.IS_TABLE_EMPTY; byte[] request = concatArrays( type.byteArrayOrdinal, getBytes(tableName)); byte[] result = invokeRequestWithRawReturn(type, request); return result != null; } /* * @see bonafide.datastore.DatastoreProxy#containsKey(java.lang.String, byte[]) */ @Override public boolean containsKey(String tableName, byte[] key) { RequestType type = RequestType.CONTAINS_KEY_IN_TABLE; byte[] request = concatArrays( type.byteArrayOrdinal, getBytes(tableName), getBytes(key.length), key); byte[] result = invokeRequestWithRawReturn(type, request); return result != null; } /* * @see bonafide.datastore.DatastoreProxy#size(java.lang.String) */ @Override public int size(String tableName) { RequestType type = RequestType.SIZE_OF_TABLE; byte[] request = concatArrays( type.byteArrayOrdinal, getBytes(tableName) ); byte[] result = invokeRequestWithRawReturn(type, request); return Ints.fromByteArray(result); } /* * @see bonafide.datastore.DatastoreProxy#getAndIncrement(java.lang.String, byte[]) */ @Override public int getAndIncrement(String tableName, String key) { RequestType type = RequestType.GET_AND_INCREMENT; byte[] request = concatArrays( type.byteArrayOrdinal, getBytes(tableName), //FIXME getBytes(key) ); byte[] result = invokeRequestWithRawReturn(type, request); return Ints.fromByteArray(result); } /** * Sends the <b>RPC</b> request to the data store and block waiting for the reply. * <p> * <code>request</code> is sent to the data store can be sent in one of two ways: * <ul> * <li>Ordered Request - if <code>type.isWrite()</code> is <code>true</code>; </li> * <li>Unordered Request - otherwise</li> * </ul> * <p> * This method is designed for extension. Classes that wish to extend behavior (such as logging) can subclass this implementation and override this method. * However it still must be called if the message is to be sent to the data store. <br/> * * TODO - set a reference to ordered and unordered request forma specification and explanation. * * @param type the type of the request (<b>RPC</b>) message sent. * @param request the request to be sent to the data store. * @return the reply from the data store. */ //TODO - catch exceptions in invokeOrdered/Unordered and throw them as RuntimeExceptions. protected byte[] invokeRequestWithRawReturn(RequestType type, byte[] request){ byte[] result = type.isRead() ?server.invokeOrdered(request) : server.invokeUnordered(request); //System.out.println("Request size:"+ request.length +" - Reply size: }" + (result != null? result.length : 0) ); return result; } //TODO - catch exceptions in invokeOrdered/Unordered and throw them as RuntimeExceptions. protected DatastoreValue invokeRequest(RequestType type, byte[] request){ byte[] result = invokeRequestWithRawReturn(type,request); return result != null ? new DatastoreValue(result) : null; } /** * Concatenates all byte arrays passed as argument. * The method signature forces the client to pass at least one array. * <p> * The final result is a newly formed array: <code>result</code> that is composed if the specified arguments arrays in order of argument passing (left to right). * @param a0 the first byte array to concatenate * @param an the remaining byte arrays. * @returns a newly created array in the form <code>a0:a1:...:an</code>. */ protected byte[] concatArrays(byte[] a0, byte[]... an){ int len = a0.length; //total length of result array for (byte[] ax : an){ len += ax.length; } byte[] version = version().byteArrayOrdinal; //Allocate result array byte[] result = new byte[len + version.length]; //Copy all argument arrays in order to the result array System.arraycopy(version, 0, result, 0, version.length); System.arraycopy(a0, 0, result, version.length, a0.length); int destPos = a0.length + version.length; for (byte[] ax: an){ int dstLength = ax.length; //destiny length System.arraycopy(ax, 0, result, destPos, dstLength); destPos += dstLength; } return result; } protected abstract DataStoreVersion version(); protected byte[] getBytes(String s){ //FIXME ByteArrayOutputStream out = new ByteArrayOutputStream(); DataOutputStream stream = new DataOutputStream(out); try { stream.writeUTF(s); return out.toByteArray(); } catch (IOException e) { throw new RuntimeException("Could not serialize string"); } } protected byte[] getBytes(Integer i){ return ByteBuffer.allocate(4).putInt(i).array(); } protected byte[] getBytes(Long l){ return ByteBuffer.allocate(8).putLong(l).array(); } }