/**
* 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.simulator.internal.p2p;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import kinetic.client.Entry;
import kinetic.client.KineticClient;
import kinetic.client.KineticException;
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.MessageType;
import com.seagate.kinetic.proto.Kinetic.Command.P2POperation;
import com.seagate.kinetic.proto.Kinetic.Command.P2POperation.Operation;
import com.seagate.kinetic.proto.Kinetic.Command.Security.ACL;
import com.seagate.kinetic.proto.Kinetic.Command.Security.ACL.Permission;
import com.seagate.kinetic.proto.Kinetic.Command.Status.StatusCode;
import com.seagate.kinetic.simulator.internal.Authorizer;
import com.seagate.kinetic.simulator.internal.KVSecurityException;
import com.seagate.kinetic.simulator.internal.KVStoreNotFound;
import com.seagate.kinetic.simulator.persist.KVValue;
import com.seagate.kinetic.simulator.persist.Store;
public class P2POperationHandler {
private final static Logger logger = Logger
.getLogger(P2POperationHandler.class.getName());
private P2PConnectionPool pool = null;
public P2POperationHandler() {
pool = new P2PConnectionPool();
}
public static boolean checkPermission(KineticMessage request,
KineticMessage respond, Map<Long, ACL> currentMap) {
boolean hasPermission = false;
Command.Builder commandBuilder = (Command.Builder) respond.getCommand();
// set reply type
commandBuilder.getHeaderBuilder().setMessageType(
MessageType.PEER2PEERPUSH_RESPONSE);
// set ack sequence
commandBuilder.getHeaderBuilder().setAckSequence(
request.getCommand().getHeader().getSequence());
// check if has permission to set security
if (currentMap == null) {
hasPermission = true;
} else {
try {
// check if client has permission
Authorizer.checkPermission(currentMap, request.getMessage()
.getHmacAuth().getIdentity(), Permission.P2POP);
hasPermission = true;
} catch (KVSecurityException e) {
commandBuilder.getStatusBuilder().setCode(
StatusCode.NOT_AUTHORIZED);
commandBuilder.getStatusBuilder().setStatusMessage(
e.getMessage());
}
}
return hasPermission;
}
public void push(Map<Long, ACL> aclmap,
Store<ByteString, ByteString, KVValue> store,
KineticMessage request, KineticMessage response) {
// get client instance
KineticClient client = this.getClient(request, response);
try {
Command.Builder commandBuilder = (Command.Builder) response
.getCommand();
/**
* perform p2p Ops if connected to peer. Otherwise,
* REMOTE_CONNECTION_ERROR code was set in the status code and
* return back to the client.
*/
if (client != null) {
// get p2p operation list
P2POperation p2pOp = request.getCommand().getBody()
.getP2POperation();
List<Operation> opList = p2pOp.getOperationList();
// response operation builder
P2POperation.Builder respP2POpBuilder = commandBuilder
.getBodyBuilder().getP2POperationBuilder();
// set default value to true.
// this will set to false when exception occurred
respP2POpBuilder.setAllChildOperationsSucceeded(true);
// loop through the list
for (Operation operation : opList) {
// response op builder
Operation.Builder respOpBuilder = Operation
.newBuilder(operation);
try {
// get entry from store
KVValue kvvalue = store.get(operation.getKey());
if (kvvalue == null) {
throw new KVStoreNotFound();
}
// construct entry to be pushed to peer
Entry entry = new Entry();
// set key
if (operation.hasNewKey()) {
// use new key
entry.setKey(operation.getNewKey().toByteArray());
} else {
// use the same key as stored
entry.setKey(kvvalue.getKeyOf().toByteArray());
}
// set value
entry.setValue(kvvalue.getData().toByteArray());
// set version, if any
if (kvvalue.hasVersion()) {
entry.getEntryMetadata().setVersion(
kvvalue.getVersion().toByteArray());
}
// set tag
if (kvvalue.hasTag()) {
entry.getEntryMetadata().setTag(
kvvalue.getTag().toByteArray());
}
if (operation.getForce()) {
// forced put ignore version
client.putForced(entry);
} else {
// if there is a version specified in op, use
// versioned put
if (operation.hasVersion()) {
// set db version
entry.getEntryMetadata().setVersion(
operation.getVersion().toByteArray());
// use store version as new version
// do versioned put
client.put(entry, kvvalue.getVersion()
.toByteArray());
} else {
// do forced put
client.putForced(entry);
}
}
// set success status
respOpBuilder.getStatusBuilder().setCode(
StatusCode.SUCCESS);
} catch (KVStoreNotFound kvne) {
logger.warning("cannot find entry from the specified key in request message...");
// set overall status
respP2POpBuilder.setAllChildOperationsSucceeded(false);
/**
* The (command) response code is set to OK even if
* exception occurred. The application can examine each
* of the operation status in the p2p response.
*/
// set individual status code
respOpBuilder.getStatusBuilder().setCode(
StatusCode.NOT_FOUND);
// set individual status message
respOpBuilder.getStatusBuilder().setStatusMessage(
"cannot find the specified key");
} catch (KineticException ke) {
/**
* errors occurred from remote peer
*/
logger.warning(ke.getLocalizedMessage());
// set overall status
respP2POpBuilder.setAllChildOperationsSucceeded(false);
/**
* The (command) response code is set to OK even if
* exception occurred. The application can examine each
* of the operation status in the p2p response.
*/
// set individual status code
respOpBuilder.getStatusBuilder().setCode(
ke.getResponseMessage().getCommand()
.getStatus().getCode());
// set individual status message
String sm = ke.getResponseMessage().getCommand()
.getStatus().getStatusMessage();
if (sm != null) {
respOpBuilder.getStatusBuilder().setStatusMessage(
sm);
}
} catch (Exception e) {
logger.log(Level.WARNING, e.getMessage(), e);
// set p2p overall status
respP2POpBuilder.setAllChildOperationsSucceeded(false);
// set individual status code
respOpBuilder.getStatusBuilder().setCode(
StatusCode.INTERNAL_ERROR);
// set individual status message
if (e.getMessage() != null) {
respOpBuilder.getStatusBuilder().setStatusMessage(
e.getMessage());
}
} finally {
// add response operation
respP2POpBuilder.addOperation(respOpBuilder.build());
}
}
}
} finally {
this.close(client);
}
}
/**
* Get peer instance.
*
* @param request
* request message
* @param response
* response message builder
* @return client instance if created and cached. Return null if any error
* occurred.
*/
private KineticClient getClient(KineticMessage request,
KineticMessage response) {
KineticClient client = null;
Command.Builder commandBuilder = (Command.Builder) response
.getCommand();
try {
client = this.pool.getKineticClient(request);
} catch (Exception e) {
// log message
logger.log(Level.WARNING, e.getMessage(), e);
// set status
commandBuilder.getStatusBuilder().setCode(
StatusCode.REMOTE_CONNECTION_ERROR);
// set status message
if (e.getMessage() != null) {
commandBuilder.getStatusBuilder().setStatusMessage(
e.getMessage());
}
}
return client;
}
private void close(KineticClient client) {
if (client == null) {
return;
}
try {
client.close();
} catch (Exception e) {
logger.warning(e.getMessage());
}
}
/**
* close connection pool.
*/
public void close() {
// close pool
this.pool.close();
}
}