/** * Copyright 2013-2015 Seagate Technology LLC. * * This Source Code Form is subject to the terms of the Mozilla * Public License, v. 2.0. If a copy of the MPL was not * distributed with this file, You can obtain one at * https://mozilla.org/MP:/2.0/. * * This program is distributed in the hope that it will be useful, * but is provided AS-IS, WITHOUT ANY WARRANTY; including without * the implied warranty of MERCHANTABILITY, NON-INFRINGEMENT or * FITNESS FOR A PARTICULAR PURPOSE. See the Mozilla Public * License for more details. * * See www.openkinetic.org for more project information */ package com.seagate.kinetic.client.internal; import java.util.ArrayList; import java.util.List; import java.util.logging.Logger; import kinetic.client.BatchOperation; import kinetic.client.CallbackHandler; import kinetic.client.ClientConfiguration; import kinetic.client.Entry; import kinetic.client.EntryMetadata; import kinetic.client.EntryNotFoundException; import kinetic.client.BatchAbortedException; import kinetic.client.KineticException; import kinetic.client.advanced.AdvancedKineticClient; import kinetic.client.advanced.PersistOption; import com.google.protobuf.ByteString; import com.seagate.kinetic.client.internal.ClientProxy.KeyRange; import com.seagate.kinetic.client.internal.ClientProxy.LCException; import com.seagate.kinetic.client.lib.ClientLogger; import com.seagate.kinetic.common.lib.KineticMessage; import com.seagate.kinetic.proto.Kinetic.Command; import com.seagate.kinetic.proto.Kinetic.Command.MessageType; import com.seagate.kinetic.proto.Kinetic.Command.Synchronization; /** * * Default KineticClient API implementation. * <p> * * @author James Hughes * @author Chiaming Yang */ public class DefaultKineticClient implements AdvancedKineticClient { // client logger private final static Logger LOG = ClientLogger.get(); // client configuration private ClientConfiguration config = null; // client proxy -- perform ops for apps. private ClientProxy client = null; /** * Constructor to instantiate a new instance of kinetic client. * * @param config * configuration for the new instance * @throws KineticException * if any internal errors occur to instantiate a new instance. */ public DefaultKineticClient(ClientConfiguration config) throws KineticException { this.config = config; // initialize the instance init(); } /** * initialize the client instance. * * @throws KineticException * if any internal errors occur to instantiate a new instance. */ private void init() throws KineticException { // create client proxy to talk to the drive client = new ClientProxy(config); //send a no-op and set connection ID. //this.connectionSetUp(); LOG.fine("kinetic client initialized, server=" + config.getHost() + ", port=" + config.getPort()); } /** * {@inheritDoc} */ @Override public Entry put(Entry entry, byte[] newVersion) throws KineticException { return this.put(entry, newVersion, PersistOption.SYNC); } /** * {@inheritDoc} */ @Override public Entry put(Entry entry, byte[] newVersion, PersistOption option) throws KineticException { // return entry Entry returnEntry = null; KineticMessage request = null; KineticMessage response = null; try { // construct put request message request = MessageFactory.createPutRequestMessage( entry, newVersion); // proto builder Command.Builder message = (Command.Builder) request.getCommand(); // set persist option setPersistOption(message, option); // send request response = this.client.request(request); // check response //MessageFactory.checkPutReply(reply, MessageType.PUT_RESPONSE); // construct return instance returnEntry = new Entry(entry.getKey(), entry.getValue(), entry.getEntryMetadata()); // set db version in entry returnEntry.getEntryMetadata().setVersion(newVersion); LOG.fine("put versioned successfully."); } catch (KineticException lce) { throw lce; } catch (Exception e) { KineticException lce = new KineticException(e.getMessage(), e); lce.setRequestMessage(request); lce.setResponseMessage(response); throw lce; } return returnEntry; } /** * {@inheritDoc} */ @Override public Entry get(byte[] key) throws KineticException { Entry entry = null; KineticMessage request = null; KineticMessage response = null; try { // create get request message request = MessageFactory.createGetRequestMessage( key, MessageType.GET); // send request response = this.client.request(request); // check response //MessageFactory.checkGetReply(response, MessageType.GET_RESPONSE); // transform message to entry entry = MessageFactory.responsetoEntry(response); } catch (EntryNotFoundException enfe) { ; } catch (Exception e) { KineticException ke = new KineticException(e.getMessage(), e); ke.setRequestMessage(request); ke.setResponseMessage(response); throw ke; } return entry; } /** * {@inheritDoc} */ @Override public Entry getNext(byte[] key) throws KineticException { Entry entry = null; KineticMessage request = null; KineticMessage response = null; try { // create get request message request = MessageFactory.createGetRequestMessage( key, MessageType.GETNEXT); // send request response = this.client.request(request); // check response //MessageFactory //.checkGetReply(response, MessageType.GETNEXT_RESPONSE); // transform message to entry entry = MessageFactory.responsetoEntry(response); } catch (EntryNotFoundException enfe) { ; } catch (Exception e) { KineticException lce = new KineticException(e.getMessage(), e); lce.setRequestMessage(request); lce.setResponseMessage(response); throw lce; } return entry; } /** * {@inheritDoc} */ @Override public Entry getPrevious(byte[] key) throws KineticException { Entry entry = null; KineticMessage request = null; KineticMessage response = null; try { // create get request message request = MessageFactory.createGetRequestMessage( key, MessageType.GETPREVIOUS); // send request response = this.client.request(request); // check response // MessageFactory.checkGetReply(response, // MessageType.GETPREVIOUS_RESPONSE); // transform message to entry entry = MessageFactory.responsetoEntry(response); } catch (EntryNotFoundException enfe) { ; } catch (Exception e) { KineticException lce = new KineticException(e.getMessage(), e); lce.setRequestMessage(request); lce.setResponseMessage(response); throw lce; } return entry; } /** * {@inheritDoc} */ @Override public List<byte[]> getKeyRange(byte[] startKey, boolean startKeyInclusive, byte[] endKey, boolean endKeyInclusive, int maxReturned) throws KineticException { // return response for the call List<byte[]> response = null; // default get key range boolean reverse = false; try { // get key range request parameter object KeyRange kr = client.new KeyRange(toByteString(startKey), startKeyInclusive, toByteString(endKey), endKeyInclusive, maxReturned, reverse); // send request to drive List<ByteString> bsList = this.client.getKeyRange(kr); // convert to return type response = toByteArrayList(bsList); } catch (KineticException ke) { throw ke; } catch (Exception e) { KineticException lce = new KineticException(e.getMessage(), e); throw lce; } return response; } /** * {@inheritDoc} */ @Override public void close() throws KineticException { try { this.client.close(); } catch (Exception e) { KineticException lce = new KineticException(e.getMessage(), e); throw lce; } } /** * {@inheritDoc} */ @Override public boolean delete(Entry entry) throws KineticException { return this.delete(entry, PersistOption.SYNC); } /** * {@inheritDoc} */ @Override public boolean delete(Entry entry, PersistOption option) throws KineticException { try { // create request message KineticMessage im = MessageFactory .createDeleteRequestMessage(entry); // proto builder Command.Builder message = (Command.Builder) im.getCommand(); // set persist option setPersistOption(message, option); // do delete op return this.doDelete(im); } catch (KineticException lce) { throw lce; } catch (Exception e) { throw new KineticException(e); } } /** * perform delete operation. * * @param request * @return * @throws KineticException */ private boolean doDelete(KineticMessage request) throws KineticException { //deleted flag boolean deleted = false; KineticMessage response = null; try { // response message response = this.client.request(request); //deleted = MessageFactory.checkDeleteReply(response); deleted = true; LOG.fine("delete entry successfully ..."); } catch (EntryNotFoundException enfe) { //entry not found returns false for delete command deleted = false; } catch (KineticException ke) { //re-throw ke throw ke; } catch (Exception e) { KineticException lce = new KineticException(e.getMessage(), e); lce.setRequestMessage(request); lce.setResponseMessage(response); throw lce; } return deleted; } /** * Convert byte array to ByteString type. * * @param bytes * byte array to be converted. * * @return converted ByteString. */ public static ByteString toByteString(byte[] bytes) { return ByteString.copyFrom(bytes); } /** * Convert a list of ByteStrig type to a list of byte[] type. * * @param bsList * a list of ByteStrig to be converted. * @return */ public List<byte[]> toByteArrayList(List<ByteString> bsList) { List<byte[]> listOfByteArray = new ArrayList<byte[]>(); for (ByteString bs : bsList) { listOfByteArray.add(bs.toByteArray()); } return listOfByteArray; } /** * Send a request operation to the Kinetic server. * <p> * * @param request * request message for a specific operation. * * @return respond message for the request operation. * * @throws LCException * if any internal error occurred. */ @Override public KineticMessage request(KineticMessage request) throws KineticException { KineticMessage respond = null; try { respond = this.client.request(request); } catch (KineticException ke) { throw ke; } catch (Exception e) { KineticException ke = new KineticException(e.getMessage(), e); ke.setRequestMessage(request); ke.setResponseMessage(respond); throw ke; } return respond; } /** * async request operation. * * @param <T> * * @param message * request message. * * @param callback * Kinetic client runtime calls the Callback.onMessage when * response message is available. * @throws KineticException * if any internal error occur. */ @Override public <T> void requestAsync(KineticMessage message, CallbackHandler<T> callback) throws KineticException { try { this.client.requestAsync(message, callback); } catch (Exception e) { throw new KineticException(e.getMessage(), e); } } /** * Get an <code>Iterable</code> of <Entry> entry in the sequence based on * the specified key range. * * @param startKey * the start key in the specified key range. * @param startKeyInclusive * true if the start key is inclusive. * @param endKey * the end key in the specified key range. * @param endKeyInclusive * true if the start key is inclusive. * * @return an <code>Iterable</code> of <Entry> entry in the sequence based * on the specified key range * * @throws KineticException * if any internal error occurred. */ public Iterable<Entry> getRange(byte[] startKey, boolean startKeyInclusive, byte[] endKey, boolean endKeyInclusive) throws KineticException { return new VersionedRange(this, startKey, startKeyInclusive, endKey, endKeyInclusive); } @Override public byte[] getVersion(byte[] key) throws KineticException { byte[] version = null; KineticMessage request = null; KineticMessage response = null; try { // create get request message request = MessageFactory.createGetVersionRequestMessage(key); // send request response = this.client.request(request); version = response.getCommand().getBody().getKeyValue() .getDbVersion().toByteArray(); } catch (Exception e) { KineticException ke = new KineticException(e.getMessage(), e); ke.setRequestMessage(request); ke.setResponseMessage(response); throw ke; } return version; } /** * {@inheritDoc} */ @Override public void putAsync(Entry entry, byte[] newVersion, CallbackHandler<Entry> handler) throws KineticException { // default as sync this.putAsync(entry, newVersion, PersistOption.SYNC, handler); } /** * {@inheritDoc} */ @Override public void putAsync(Entry entry, byte[] newVersion, PersistOption option, CallbackHandler<Entry> handler) throws KineticException { // construct put request message KineticMessage km = MessageFactory.createPutRequestMessage(entry, newVersion); // proto builder Command.Builder message = (Command.Builder) km.getCommand(); // set persist option setPersistOption(message, option); // send request to the drive this.client.requestAsync(km, handler); } public void batchPutAsync(Entry entry, byte[] newVersion, CallbackHandler<Entry> handler, int batchId) throws KineticException { // construct put request message KineticMessage km = MessageFactory.createPutRequestMessage(entry, newVersion); // proto builder Command.Builder command = (Command.Builder) km.getCommand(); // set batch id command.getHeaderBuilder().setBatchID(batchId); // send request to the drive this.client.requestAsync(km, handler); } public void batchPut(Entry entry, byte[] newVersion, int batchId) throws KineticException { // construct put request message KineticMessage km = MessageFactory.createPutRequestMessage(entry, newVersion); // proto builder Command.Builder command = (Command.Builder) km.getCommand(); // set batch id command.getHeaderBuilder().setBatchID(batchId); // send request to the drive this.client.requestNoAck(km); } public void batchPutForced(Entry entry, int batchId) throws KineticException { byte[] newVersion = null; if (entry.getEntryMetadata() != null) { newVersion = entry.getEntryMetadata().getVersion(); } // construct put request message KineticMessage km = MessageFactory.createPutRequestMessage(entry, newVersion); Command.Builder commandBuilder = (Command.Builder) km.getCommand(); // set batchId commandBuilder.getHeaderBuilder().setBatchID(batchId); // set force bit commandBuilder.getBodyBuilder().getKeyValueBuilder().setForce(true); this.client.requestNoAck(km); } /** * {@inheritDoc} */ @Override public void getAsync(byte[] key, CallbackHandler<Entry> handler) throws KineticException { // construct put request message KineticMessage message = MessageFactory.createGetRequestMessage(key, MessageType.GET); this.client.requestAsync(message, handler); } /** * {@inheritDoc} */ @Override public void deleteAsync(Entry entry, CallbackHandler<Boolean> handler) throws KineticException { this.deleteAsync(entry, PersistOption.SYNC, handler); } /** * {@inheritDoc} */ @Override public void deleteAsync(Entry entry, PersistOption option, CallbackHandler<Boolean> handler) throws KineticException { KineticMessage km = MessageFactory .createDeleteRequestMessage(entry); // proto builder Command.Builder message = (Command.Builder) km.getCommand(); // set persist option setPersistOption(message, option); this.client.requestAsync(km, handler); } public void batchDeleteAsync(Entry entry, CallbackHandler<Boolean> handler, int batchId) throws KineticException { KineticMessage km = MessageFactory.createDeleteRequestMessage(entry); // proto builder Command.Builder message = (Command.Builder) km.getCommand(); // set batch id message.getHeaderBuilder().setBatchID(batchId); this.client.requestAsync(km, handler); } public void batchDelete(Entry entry, int batchId) throws KineticException { KineticMessage km = MessageFactory.createDeleteRequestMessage(entry); // proto builder Command.Builder message = (Command.Builder) km.getCommand(); // set batch id message.getHeaderBuilder().setBatchID(batchId); this.client.requestNoAck(km); } /** * {@inheritDoc} */ @Override public void getNextAsync(byte[] key, CallbackHandler<Entry> handler) throws KineticException { // construct put request message KineticMessage message = MessageFactory.createGetRequestMessage(key, MessageType.GETNEXT); this.client.requestAsync(message, handler); } /** * {@inheritDoc} */ @Override public void getPreviousAsync(byte[] key, CallbackHandler<Entry> handler) throws KineticException { // construct getPrevious request message KineticMessage message = MessageFactory.createGetRequestMessage(key, MessageType.GETPREVIOUS); this.client.requestAsync(message, handler); } /** * {@inheritDoc} */ @Override public void getKeyRangeAsync(byte[] startKey, boolean startKeyInclusive, byte[] endKey, boolean endKeyInclusive, int maxKeys, CallbackHandler<List<byte[]>> handler) throws KineticException { KineticMessage message = MessageFactory.createGetKeyRangeMessage( startKey, startKeyInclusive, endKey, endKeyInclusive, maxKeys, false); this.client.requestAsync(message, handler); } /** * {@inheritDoc} */ @Override public EntryMetadata getMetadata(byte[] key) throws KineticException { EntryMetadata metadata = null; KineticMessage request = null; KineticMessage response = null; try { // create get metadata request message request = MessageFactory.createGetMetadataMessage( key, MessageType.GET); // send request response = this.client.request(request); // check response //MessageFactory.checkGetReply(response, MessageType.GET_RESPONSE); // transform message to metadata metadata = MessageFactory.responsetoEntryMetadata(response); } catch (EntryNotFoundException enfe) { ; } catch (Exception e) { KineticException ke = new KineticException(e.getMessage(), e); ke.setRequestMessage(request); ke.setResponseMessage(response); throw ke; } return metadata; } /** * {@inheritDoc} */ @Override public void getMetadataAsync(byte[] key, CallbackHandler<EntryMetadata> handler) throws KineticException { // construct get metadata request message KineticMessage message = MessageFactory.createGetMetadataMessage(key, MessageType.GET); this.client.requestAsync(message, handler); } /** * {@inheritDoc} */ @Override public Entry putForced(Entry entry) throws KineticException { return this.putForced(entry, PersistOption.SYNC); } /** * {@inheritDoc} */ @Override public Entry putForced(Entry entry, PersistOption option) throws KineticException { byte[] newVersion = null; KineticMessage request = null; KineticMessage response = null; if (entry.getEntryMetadata() != null) { newVersion = entry.getEntryMetadata().getVersion(); } try { // construct put request message request = MessageFactory.createPutRequestMessage( entry, newVersion); Command.Builder commandBuilder = (Command.Builder) request.getCommand(); // set force bit commandBuilder.getBodyBuilder().getKeyValueBuilder() .setForce(true); // set persist option setPersistOption(commandBuilder, option); // send request response = this.client.request(request); // check response //MessageFactory.checkPutReply(reply, MessageType.PUT_RESPONSE); LOG.fine("put versioned successfully."); } catch (KineticException lce) { throw lce; } catch (Exception e) { KineticException lce = new KineticException(e.getMessage(), e); lce.setRequestMessage(request); lce.setResponseMessage(response); throw lce; } return entry; } /** * {@inheritDoc} */ @Override public void putForcedAsync(Entry entry, CallbackHandler<Entry> handler) throws KineticException { this.putForcedAsync(entry, PersistOption.SYNC, handler); } /** * {@inheritDoc} */ @Override public void putForcedAsync(Entry entry, PersistOption option, CallbackHandler<Entry> handler) throws KineticException { byte[] newVersion = null; if (entry.getEntryMetadata() != null) { newVersion = entry.getEntryMetadata().getVersion(); } // construct put request message KineticMessage km = MessageFactory.createPutRequestMessage(entry, newVersion); Command.Builder commandBuilder = (Command.Builder) km.getCommand(); // set force bit commandBuilder.getBodyBuilder().getKeyValueBuilder().setForce(true); // set persist option setPersistOption(commandBuilder, option); this.client.requestAsync(km, handler); } public void batchPutForcedAsync(Entry entry, CallbackHandler<Entry> handler, int batchId) throws KineticException { byte[] newVersion = null; if (entry.getEntryMetadata() != null) { newVersion = entry.getEntryMetadata().getVersion(); } // construct put request message KineticMessage km = MessageFactory.createPutRequestMessage(entry, newVersion); Command.Builder commandBuilder = (Command.Builder) km.getCommand(); // set batchId commandBuilder.getHeaderBuilder().setBatchID(batchId); // set force bit commandBuilder.getBodyBuilder().getKeyValueBuilder().setForce(true); this.client.requestAsync(km, handler); } /** * {@inheritDoc} */ @Override public boolean deleteForced(byte[] key) throws KineticException { return this.deleteForced(key, PersistOption.SYNC); } /** * {@inheritDoc} */ @Override public boolean deleteForced(byte[] key, PersistOption option) throws KineticException { // create force delete request message KineticMessage km = MessageFactory .createForceDeleteRequestMessage(key); // get command builder Command.Builder request = (Command.Builder) km.getCommand(); // set persist option setPersistOption(request, option); // do delete return this.doDelete(km); } /** * {@inheritDoc} */ @Override public void deleteForcedAsync(byte[] key, CallbackHandler<Boolean> handler) throws KineticException { this.deleteForcedAsync(key, PersistOption.SYNC, handler); } /** * {@inheritDoc} */ @Override public void deleteForcedAsync(byte[] key, PersistOption option, CallbackHandler<Boolean> handler) throws KineticException { // create force delete request message KineticMessage km = MessageFactory .createForceDeleteRequestMessage(key); Command.Builder request = (Command.Builder) km.getCommand(); // set persist option setPersistOption(request, option); // do async delete this.client.requestAsync(km, handler); } public void batchDeleteForcedAsync(byte[] key, CallbackHandler<Boolean> handler, int batchId) throws KineticException { // create force delete request message KineticMessage km = MessageFactory .createForceDeleteRequestMessage(key); Command.Builder request = (Command.Builder) km.getCommand(); request.getHeaderBuilder().setBatchID(batchId); // do async delete this.client.requestAsync(km, handler); } public void batchDeleteForced(byte[] key, int batchId) throws KineticException { // create force delete request message KineticMessage km = MessageFactory.createForceDeleteRequestMessage(key); Command.Builder request = (Command.Builder) km.getCommand(); request.getHeaderBuilder().setBatchID(batchId); // do async delete this.client.requestNoAck(km); } /** * {@inheritDoc} */ @Override public long noop() throws KineticException { long time = System.currentTimeMillis(); // create get request message KineticMessage request = MessageFactory.createNoOpRequestMessage(); // send request this.client.request(request); // check response // MessageFactory.checkNoOpReply(response); // no op round trip time time = System.currentTimeMillis() - time; return time; } /** * {@inheritDoc} */ @Override public void flush() throws KineticException { // create get request message KineticMessage request = MessageFactory.createFlushDataRequestMessage(); // send request this.client.request(request); } /** * Set persist option flag to the protocol buffer message. * * @see PersistOption */ private static void setPersistOption(Command.Builder message, PersistOption option) { if (option == null) { throw new NullPointerException("persist option cannot be null"); } switch (option) { case SYNC: message.getBodyBuilder().getKeyValueBuilder() .setSynchronization(Synchronization.WRITETHROUGH); break; case ASYNC: message.getBodyBuilder().getKeyValueBuilder() .setSynchronization(Synchronization.WRITEBACK); break; case FLUSH: message.getBodyBuilder().getKeyValueBuilder() .setSynchronization(Synchronization.FLUSH); break; default: message.getBodyBuilder().getKeyValueBuilder() .setSynchronization(Synchronization.WRITETHROUGH); break; } } /** * {@inheritDoc} */ @Override public List<byte[]> getKeyRangeReversed(byte[] startKey, boolean startKeyInclusive, byte[] endKey, boolean endKeyInclusive, int maxKeys) throws KineticException { // return type List<byte[]> response = null; // set reverse flag boolean reverse = true; try { // construct request parameter object KeyRange kr = client.new KeyRange(toByteString(startKey), startKeyInclusive, toByteString(endKey), endKeyInclusive, maxKeys, reverse); // send request List<ByteString> bsList = this.client.getKeyRange(kr); // convert to return type response = toByteArrayList(bsList); } catch (KineticException ke) { throw ke; } catch (Exception e) { KineticException lce = new KineticException(e.getMessage(), e); throw lce; } return response; } /** * {@inheritDoc} */ @Override public void getKeyRangeReversedAsync(byte[] startKey, boolean startKeyInclusive, byte[] endKey, boolean endKeyInclusive, int maxKeys, CallbackHandler<List<byte[]>> handler) throws KineticException { KineticMessage message = MessageFactory.createGetKeyRangeMessage( startKey, startKeyInclusive, endKey, endKeyInclusive, maxKeys, true); this.client.requestAsync(message, handler); } /** * start a new batch operation. * * @throws KineticException * if any error occurred. */ void startBatchOperation(int batchId) throws KineticException { KineticMessage request = null; KineticMessage response = null; // create get request message request = MessageFactory.createStartBatchRequestMessage(batchId); // send request response = this.client.request(request); // check response MessageFactory.checkReply(request, response); } /** * commit the batch operation. * * @throws KineticException * if any error occurred. */ void endBatchOperation(int batchId, int count) throws KineticException { KineticMessage request = null; KineticMessage response = null; // create request message request = MessageFactory.createEndBatchRequestMessage(batchId, count); try { // send request response = this.client.request(request); // check response MessageFactory.checkReply(request, response); } catch (KineticException ke) { this.handleBatchException(ke); } } private void handleBatchException(KineticException ke) throws KineticException { if (ke.getResponseMessage() != null) { BatchAbortedException bae = null; String msg = ke.getResponseMessage().getCommand().getStatus() .getStatusMessage(); bae = new BatchAbortedException(msg); List<Long> slist = ke.getResponseMessage().getCommand().getBody() .getBatch().getSequenceList(); long fs = ke.getResponseMessage().getCommand().getBody().getBatch() .getFailedSequence(); int index = -1; for (int i = 0; i < slist.size(); i++) { if (slist.get(i) == fs) { index = i; break; } } bae.setFailedOperationIndex(index); bae.setRequestMessage(ke.getRequestMessage()); bae.setResponseMessage(ke.getResponseMessage()); // set index throw bae; } else { throw ke; } } void abortBatchOperation(int batchId) throws KineticException { KineticMessage request = null; KineticMessage response = null; // create get request message request = MessageFactory.createAbortBatchRequestMessage(batchId); // send request response = this.client.request(request); // check response MessageFactory.checkReply(request, response); } /** * {@inheritDoc} */ @Override public BatchOperation createBatchOperation() throws KineticException { // create and return a new instance of BatchOperation implementation return new DefaultBatchOperation(this); } }