/**
* 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 kinetic.client.ClusterVersionFailureException;
import kinetic.client.Entry;
import kinetic.client.EntryMetadata;
import kinetic.client.EntryNotFoundException;
import kinetic.client.KineticException;
import kinetic.client.VersionMismatchException;
import com.google.protobuf.ByteString;
import com.seagate.kinetic.common.lib.KineticMessage;
import com.seagate.kinetic.proto.Kinetic.Command;
import com.seagate.kinetic.proto.Kinetic.Command.Algorithm;
import com.seagate.kinetic.proto.Kinetic.Command.KeyValue;
import com.seagate.kinetic.proto.Kinetic.Command.MessageType;
import com.seagate.kinetic.proto.Kinetic.Command.Range;
import com.seagate.kinetic.proto.Kinetic.Command.Status.StatusCode;
import com.seagate.kinetic.proto.Kinetic.Command.Synchronization;
import com.seagate.kinetic.proto.Kinetic.Message;
import com.seagate.kinetic.proto.Kinetic.Message.AuthType;
/**
* Kinetic Message factory for the Java API client runtime implementation.
*
* @author chiaming
*
*/
public class MessageFactory {
// persist synchronization mode
private static Synchronization synchronization = Synchronization.WRITEBACK;
// override system property
static {
boolean async = Boolean.getBoolean("kinetic.persist.async");
if (async) {
synchronization = Synchronization.WRITEBACK;
}
}
public static KineticMessage createPutRequestMessage(Entry entry,
byte[] newVersion) throws KineticException {
// new message holder
KineticMessage kineticMessage = new KineticMessage();
// new message
Message.Builder message = Message.newBuilder();
message.setAuthType(AuthType.HMACAUTH);
// create command builder
Command.Builder commandBuilder = Command.newBuilder();
// set proto message
kineticMessage.setMessage(message);
// set command
kineticMessage.setCommand(commandBuilder);
// set message type
commandBuilder.getHeaderBuilder()
.setMessageType(MessageType.PUT);
// set KeyValue in body
KeyValue.Builder kv = commandBuilder.getBodyBuilder()
.getKeyValueBuilder();
try {
// set key
kv.setKey(ByteString.copyFrom(entry.getKey()));
// set db version
byte[] dbversion = entry.getEntryMetadata().getVersion();
if (dbversion != null && dbversion.length > 0) {
kv.setDbVersion(ByteString.copyFrom(dbversion));
}
// set new version
if (newVersion != null && newVersion.length > 0) {
kv.setNewVersion(ByteString.copyFrom(newVersion));
}
// set value
if (entry.getValue() != null) {
// message.setValue(ByteString.copyFrom(entry.getValue()));
kineticMessage.setValue(entry.getValue());
}
// set tag
if (entry.getEntryMetadata().getTag() != null) {
kv.setTag(ByteString
.copyFrom(entry.getEntryMetadata().getTag()));
}
if (entry.getEntryMetadata().getAlgorithm() != null) {
kv.setAlgorithm(Algorithm.valueOf(entry.getEntryMetadata()
.getAlgorithm()));
}
// set synchronization mode if not set
if (kv.hasSynchronization() == false) {
// logger.info("setting sync flag: " +
// synchronization.toString());
kv.setSynchronization(synchronization);
}
} catch (Exception e) {
KineticException lce = new KineticException(e.getMessage(), e);
throw lce;
}
return kineticMessage;
}
public static boolean checkDeleteReply(KineticMessage request, KineticMessage reply)
throws KineticException {
try {
checkReply (request, reply);
} catch (EntryNotFoundException nfe) {
return false;
} catch (KineticException ke) {
throw ke;
}
return true;
}
public static KineticMessage createGetRequestMessage(byte[] key,
MessageType requestType) throws KineticException {
KineticMessage kineticMessage = createKineticMessageWithBuilder();
Command.Builder commandBuilder = (Command.Builder) kineticMessage.getCommand();
commandBuilder.getHeaderBuilder()
.setMessageType(requestType);
commandBuilder.getBodyBuilder().getKeyValueBuilder()
.setKey(ByteString.copyFrom(key));
return kineticMessage;
}
public static KineticMessage createGetKeyRangeMessage(byte[] startKey,
boolean startKeyInclusive, byte[] endKey, boolean endKeyInclusive,
int maxKeys, boolean reverse) throws KineticException {
KineticMessage kineticMessage = createKineticMessageWithBuilder();
Command.Builder commandBuilder = (Command.Builder) kineticMessage.getCommand();
// set message type
commandBuilder.getHeaderBuilder()
.setMessageType(MessageType.GETKEYRANGE);
Range.Builder op = commandBuilder.getBodyBuilder()
.getRangeBuilder();
op.setStartKey(ByteString.copyFrom(startKey));
op.setEndKey(ByteString.copyFrom(endKey));
op.setStartKeyInclusive(startKeyInclusive);
op.setEndKeyInclusive(endKeyInclusive);
op.setMaxReturned(maxKeys);
op.setReverse(reverse);
// request.getCommandBuilder().getBodyBuilder().getKeyValueBuilder().get
return kineticMessage;
}
public static KineticMessage createGetMetadataMessage(byte[] key,
MessageType requestType) throws KineticException {
KineticMessage kineticMessage = createGetRequestMessage(key, requestType);
Command.Builder commandBuilder = (Command.Builder) kineticMessage.getCommand();
commandBuilder.getBodyBuilder().getKeyValueBuilder()
.setMetadataOnly(true);
return kineticMessage;
}
public static KineticMessage createDeleteRequestMessage(Entry entry)
throws KineticException {
KineticMessage kineticMessage = createKineticMessageWithBuilder();
Command.Builder commandBuilder = (Command.Builder) kineticMessage.getCommand();
// set message type
commandBuilder.getHeaderBuilder()
.setMessageType(MessageType.DELETE);
// set key
commandBuilder.getBodyBuilder().getKeyValueBuilder()
.setKey(ByteString.copyFrom(entry.getKey()));
// set version
byte[] version = entry.getEntryMetadata().getVersion();
if (version != null && version.length > 0) {
commandBuilder.getBodyBuilder().getKeyValueBuilder()
.setDbVersion(ByteString.copyFrom(version));
}
// set synchronization mode if not set
if (commandBuilder.getBodyBuilder().getKeyValueBuilder()
.hasSynchronization() == false) {
// logger.info("setting sync flag: " + synchronization.toString());
commandBuilder.getBodyBuilder().getKeyValueBuilder()
.setSynchronization(synchronization);
}
// delete request
return kineticMessage;
}
public static KineticMessage createForceDeleteRequestMessage(byte[] key)
throws KineticException {
KineticMessage kineticMessage = createKineticMessageWithBuilder();
Command.Builder commandBuilder = (Command.Builder) kineticMessage.getCommand();
// set message type
commandBuilder.getHeaderBuilder().setMessageType(MessageType.DELETE);
// set key
commandBuilder.getBodyBuilder().getKeyValueBuilder()
.setKey(ByteString.copyFrom(key));
// set force bit
commandBuilder.getBodyBuilder().getKeyValueBuilder()
.setForce(true);
// set synchronization mode if not set
if (commandBuilder.getBodyBuilder().getKeyValueBuilder()
.hasSynchronization() == false) {
// logger.info("setting sync flag: " + synchronization.toString());
commandBuilder.getBodyBuilder().getKeyValueBuilder()
.setSynchronization(synchronization);
}
// delete request
return kineticMessage;
}
public static Entry responsetoEntry(KineticMessage kineticMessage) {
Command response = (Command) kineticMessage.getCommand();
if (response.getStatus().getCode() == StatusCode.NOT_FOUND) {
return null;
}
Entry entry = new Entry();
KeyValue kv = response.getBody().getKeyValue();
// set key
entry.setKey(kv.getKey().toByteArray());
// set value
if (kineticMessage.getValue() != null) {
// entry.setValue(response.getValue().toByteArray());
entry.setValue(kineticMessage.getValue());
} else {
entry.setValue(new byte[0]);
}
// set db version
if (kv.getDbVersion() != null && kv.getDbVersion().size() > 0) {
entry.getEntryMetadata()
.setVersion(kv.getDbVersion().toByteArray());
}
if (kv.getTag() != null && kv.getTag().size() > 0) {
entry.getEntryMetadata().setTag(kv.getTag().toByteArray());
}
if (kv.getAlgorithm() != null) {
entry.getEntryMetadata().setAlgorithm(kv.getAlgorithm().toString());
}
return entry;
}
public static EntryMetadata responsetoEntryMetadata(
KineticMessage response) {
if (response.getCommand().getStatus().getCode() == StatusCode.NOT_FOUND) {
return null;
}
EntryMetadata metadata = new EntryMetadata();
KeyValue kv = response.getCommand().getBody().getKeyValue();
// set db version
if (kv.getDbVersion() != null && kv.getDbVersion().size() > 0) {
metadata.setVersion(kv.getDbVersion().toByteArray());
}
if (kv.getTag() != null && kv.getTag().size() > 0) {
metadata.setTag(kv.getTag().toByteArray());
}
if (kv.getAlgorithm() != null) {
metadata.setAlgorithm(kv.getAlgorithm().toString());
}
return metadata;
}
/**
* Check the response message status.
*
* @param reply the response message from drive/simulator
*
* @throws KineticException if status code is not equal to <code>StatusCode.SUCCESS</code>
*/
public static void checkReply(KineticMessage request, KineticMessage reply)
throws KineticException {
//request message type
MessageType requestType = request.getCommand().getHeader().getMessageType();
//response message type
MessageType responseType = reply.getCommand().getHeader().getMessageType();
//check message type
//see .proto for message type definition rules
if (responseType.getNumber() != (requestType.getNumber()-1)) {
String msg =
"Received wrong message type., received="
+ responseType
+ ", expected=" + requestType;
throw new KineticException (msg);
}
//check if contains status message
if (!reply.getCommand().hasStatus()) {
throw new KineticException("No status was set");
}
//get status code
StatusCode statusCode = reply.getCommand().getStatus().getCode();
//if success, all is fine. simply return
if (statusCode == StatusCode.SUCCESS) {
return;
}
//set standard exception message
String errorMessage = "Kinetic Command Exception: "
+ statusCode
+ ": "
+ reply.getCommand().getStatus()
.getStatusMessage();
switch (statusCode) {
case VERSION_MISMATCH:
//throw version mismatch exception
throw new VersionMismatchException(errorMessage);
case VERSION_FAILURE:
//throw cluster version exception
throw new ClusterVersionFailureException (errorMessage);
case NOT_FOUND:
//entry not found
throw new EntryNotFoundException (errorMessage);
default:
//throw normal kinetic exception
throw new KineticException (errorMessage);
}
}
public static KineticMessage createNoOpRequestMessage()
throws KineticException {
KineticMessage kineticMessage = createKineticMessageWithBuilder();
Command.Builder request = (Command.Builder) kineticMessage.getCommand();
request.getHeaderBuilder().setMessageType(MessageType.NOOP);
return kineticMessage;
}
public static KineticMessage createGetVersionRequestMessage(byte[] key)
throws KineticException {
KineticMessage kineticMessage = createKineticMessageWithBuilder();
Command.Builder request = (Command.Builder) kineticMessage.getCommand();
request.getHeaderBuilder().setMessageType(MessageType.GETVERSION);
request.getBodyBuilder().getKeyValueBuilder()
.setKey(ByteString.copyFrom(key));
return kineticMessage;
}
public static KineticMessage createFlushDataRequestMessage()
throws KineticException {
KineticMessage kineticMessage = createKineticMessageWithBuilder();
Command.Builder request = (Command.Builder) kineticMessage.getCommand();
request.getHeaderBuilder().setMessageType(MessageType.FLUSHALLDATA);
return kineticMessage;
}
public static KineticMessage createStartBatchRequestMessage(int batchId)
throws KineticException {
KineticMessage kineticMessage = createKineticMessageWithBuilder();
Command.Builder request = (Command.Builder) kineticMessage.getCommand();
request.getHeaderBuilder().setMessageType(MessageType.START_BATCH);
request.getHeaderBuilder().setBatchID(batchId);
return kineticMessage;
}
public static KineticMessage createEndBatchRequestMessage(int batchId,
int count)
throws KineticException {
KineticMessage kineticMessage = createKineticMessageWithBuilder();
Command.Builder request = (Command.Builder) kineticMessage.getCommand();
request.getHeaderBuilder().setMessageType(MessageType.END_BATCH);
request.getHeaderBuilder().setBatchID(batchId);
request.getBodyBuilder().getBatchBuilder().setCount(count);
return kineticMessage;
}
public static KineticMessage createAbortBatchRequestMessage(int batchId)
throws KineticException {
KineticMessage kineticMessage = createKineticMessageWithBuilder();
Command.Builder request = (Command.Builder) kineticMessage.getCommand();
request.getHeaderBuilder().setMessageType(MessageType.ABORT_BATCH);
request.getHeaderBuilder().setBatchID(batchId);
return kineticMessage;
}
/**
* create an internal message with empty builder message.
*
* @return an internal message with empty builder message
*/
public static KineticMessage createKineticMessageWithBuilder() {
// new instance of internal message
KineticMessage kineticMessage = new KineticMessage();
// new builder message
Message.Builder message = Message.newBuilder();
// set to im
kineticMessage.setMessage(message);
//set hmac auth type
message.setAuthType(AuthType.HMACAUTH);
// create command builder
Command.Builder commandBuilder = Command.newBuilder();
// set command
kineticMessage.setCommand(commandBuilder);
return kineticMessage;
}
}