Commit aa7cc92f authored by chiaming2000's avatar chiaming2000
Browse files

Simulator code clean up: Introduced RequestContext class as a container

to encapsulate each request data and process the request within its
context.
parent c0549523
Loading
Loading
Loading
Loading
+305 −0
Original line number Diff line number Diff line
/**
 * 
 * Copyright (C) 2014 Seagate Technology.
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 */
package com.seagate.kinetic.simulator.internal;

import java.security.Key;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.google.protobuf.ByteString;
import com.seagate.kinetic.common.lib.Hmac;
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.PinOperation.PinOpType;
import com.seagate.kinetic.proto.Kinetic.Command.Status.StatusCode;
import com.seagate.kinetic.proto.Kinetic.Message;
import com.seagate.kinetic.proto.Kinetic.Message.AuthType;
import com.seagate.kinetic.simulator.internal.handler.ServiceException;
import com.seagate.kinetic.simulator.lib.HeaderOp;

/**
 * Request context is a container that encapsulate each request information to
 * process the request command.
 * 
 * @author chiaming
 *
 */
public class RequestContext {
    
    private final static Logger logger = Logger.getLogger(RequestContext.class
            .getName());

    // simulator engine
    private SimulatorEngine engine = null;

    // response message
    private KineticMessage request = null;
    
    // create response message
    private KineticMessage response = null;;
    
    // response command builder
    private Command.Builder commandBuilder = null;;

    // response message builder
    private Message.Builder messageBuilder = null;

    // user identity for this message
    private long userId = -1;

    // get user key
    private Key key = null;;

    private MessageType mtype = null;;

    /**
     * The constructor.
     * 
     * @param engine
     *            simulator engine.
     * 
     * @param request
     *            request message
     */
    public RequestContext(SimulatorEngine engine, KineticMessage request) {
        this.engine = engine;
        this.request = request;

        this.init();
    }

    /**
     * Get request message for this context.
     * 
     * @return request message for this context
     */
    public KineticMessage getRequestMessage() {
        return this.request;
    }

    /**
     * Get response message for this context.
     * 
     * @return response message for this context.
     */
    public KineticMessage getResponseMessage() {
        return this.response;
    }

    /**
     * Get message type for this request.
     * 
     * @return message type for this request
     */
    public MessageType getMessageType() {
        return this.mtype;
    }

    /**
     * Get user Id for this request message.
     * 
     * @return user Id for this request.
     */
    public long getUserId() {
        return this.userId;
    }

    /**
     * Get user key for this request message.
     * 
     * @return user key for this request message.
     */
    public Key getUserKey() {
        return this.key;
    }

    /**
     * Get response command builder for this context.
     * 
     * @return response command builder for this context.
     */
    public Command.Builder getCommandBuilder() {
        return this.commandBuilder;
    }

    /**
     * Get response message builder for this context.
     * 
     * @return response message builder for this context.
     */
    public Message.Builder getMessgeBuilder() {
        return this.messageBuilder;
    }
    
    /**
     * initialize context for this request message.
     * 
     */
    private void init() {

        // create response message
        response = createKineticMessageWithBuilder();

        // get response command builder
        commandBuilder = (Command.Builder) response.getCommand();

        // get response message builder
        messageBuilder = (Message.Builder) response.getMessage();
        
        // get user id for this request
        userId = request.getMessage().getHmacAuth().getIdentity();
        
        // get key for this request
        key = this.engine.getHmacKeyMap().get(Long.valueOf(userId));

        // get message type for this request
        mtype = request.getCommand().getHeader().getMessageType();
    }
    
    /**
     * 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;
    }

    /**
     * Pre process the request message.
     * 
     * @throws Exception
     *             if any internal error occurred.
     */
    public void preProcessRequest() throws Exception {

        HeaderOp.checkHeader(this.request, this.response, key,
                this.engine.getClusterVersion());

        checkDeviceLocked();
    }

    /**
     * check if the device is locked.
     * 
     * @param kmreq
     * @param kmresp
     * @throws DeviceLockedException
     */
    private void checkDeviceLocked()
            throws DeviceLockedException {

        if (this.engine.getDeviceLocked() == false) {
            return;
        }

        PinOpType pinOpType = request.getCommand().getBody().getPinOp()
                .getPinOpType();

        if (pinOpType != PinOpType.UNLOCK_PINOP
                && pinOpType != PinOpType.LOCK_PINOP) {

            // set device locked status code
            commandBuilder.getStatusBuilder().setCode(StatusCode.DEVICE_LOCKED);

            // set status message
            commandBuilder.getStatusBuilder().setStatusMessage(
                    "Device is locked");

            throw new DeviceLockedException();
        }

    }

    /**
     * Process the request message within the current context.
     * 
     * @throws ServiceException
     *             if any internal error occurred.
     */
    public void processRequest() throws ServiceException {

        // dispatch to handler to process the request
        this.engine.getCommandManager().getHandler(this.mtype)
                .processRequest(request, response);
    }

    /**
     * Post process the request message.
     */
    public void postProcessRequest() {
        this.finalizeResponseMessage();
    }

    /**
     * Finalize the response message.
     * 
     */
    private void finalizeResponseMessage() {

        try {
            // get command byte stirng
            ByteString commandByteString = commandBuilder.build()
                    .toByteString();

            // get command byte[]
            byte[] commandByte = commandByteString.toByteArray();

            // require Hmac calculation ?
            if (request.getMessage().getAuthType() == AuthType.HMACAUTH) {

                // calculate hmac
                ByteString hmac = Hmac.calc(commandByte, key);

                // set identity
                messageBuilder.getHmacAuthBuilder().setIdentity(userId);

                // set hmac
                messageBuilder.getHmacAuthBuilder().setHmac(hmac);
            }

            // set command bytes
            messageBuilder.setCommandBytes(commandByteString);
        } catch (Exception e) {
            logger.log(Level.WARNING, e.getMessage(), e);
        }
    }

}
+50 −126
Original line number Diff line number Diff line
@@ -32,8 +32,6 @@ import java.util.logging.Logger;

import kinetic.simulator.SimulatorConfiguration;

import com.google.protobuf.ByteString;
import com.seagate.kinetic.common.lib.Hmac;
import com.seagate.kinetic.common.lib.KineticMessage;
import com.seagate.kinetic.heartbeat.message.ByteCounter;
import com.seagate.kinetic.heartbeat.message.OperationCounter;
@@ -41,7 +39,6 @@ import com.seagate.kinetic.proto.Kinetic.Command;
import com.seagate.kinetic.proto.Kinetic.Command.GetLog.Configuration;
import com.seagate.kinetic.proto.Kinetic.Command.GetLog.Limits;
import com.seagate.kinetic.proto.Kinetic.Command.MessageType;
import com.seagate.kinetic.proto.Kinetic.Command.PinOperation.PinOpType;
import com.seagate.kinetic.proto.Kinetic.Command.Security.ACL;
import com.seagate.kinetic.proto.Kinetic.Command.Status.StatusCode;
import com.seagate.kinetic.proto.Kinetic.Local;
@@ -52,7 +49,6 @@ import com.seagate.kinetic.simulator.internal.handler.CommandManager;
import com.seagate.kinetic.simulator.io.provider.nio.NioEventLoopGroupManager;
import com.seagate.kinetic.simulator.io.provider.spi.MessageService;
import com.seagate.kinetic.simulator.io.provider.spi.TransportProvider;
import com.seagate.kinetic.simulator.lib.HeaderOp;
import com.seagate.kinetic.simulator.persist.Store;
import com.seagate.kinetic.simulator.persist.StoreFactory;
import com.seagate.kinetic.simulator.utility.ConfigurationUtil;
@@ -311,29 +307,11 @@ public class SimulatorEngine implements MessageService {
    }

    private void initHandlers() {

        this.manager = new CommandManager(this);
    }

        // this.pinOpHandler = new PinOpHandler();
        // this.pinOpHandler.init(this);
        //
        // this.flushHandler = new FlushOpHandler();
        // this.flushHandler.init(this);
        //
        // this.noOpHandler = new NoOpHandler();
        // this.noOpHandler.init(this);
        //
        // this.keyValueOpHandler = new KeyValueOpHandler();
        // this.keyValueOpHandler.init(this);
        //
        // this.rangeOpHandler = new RangeOpHandler();
        // this.rangeOpHandler.init(this);
        //
        // this.securityOpHandler = new SecurityOpHandler();
        // this.securityOpHandler.init(this);
        //
        // this.setupOpHandler = new SetupOpHandler();
        // this.setupOpHandler.init(this);
    public CommandManager getCommandManager() {
        return this.manager;
    }

    public boolean useNio() {
@@ -369,6 +347,10 @@ public class SimulatorEngine implements MessageService {
        this.clusterVersion = cversion;
    }

    public long getClusterVersion() {
        return this.clusterVersion;
    }

    public SecurityPin getSecurityPin() {
        return this.securityPin;
    }
@@ -466,83 +448,52 @@ public class SimulatorEngine implements MessageService {
    @Override
    public KineticMessage processRequest(KineticMessage kmreq) {

        // create response message
        KineticMessage kmresp = createKineticMessageWithBuilder();

        // get command builder
        Command.Builder commandBuilder = (Command.Builder) kmresp.getCommand();

        // get message builder
        Message.Builder messageBuilder = (Message.Builder) kmresp.getMessage();

        // get user identity for this message
        long userId = kmreq.getMessage().getHmacAuth().getIdentity();

        // get user key
        Key key = this.hmacKeyMap.get(Long.valueOf(userId));

        MessageType mtype = kmreq.getCommand().getHeader().getMessageType();

        // response message type
        commandBuilder.getHeaderBuilder().setMessageType(
                MessageType.valueOf(mtype.getNumber() - 1));
        // create request context
        RequestContext context = new RequestContext(this, kmreq);

        try {

            HeaderOp.checkHeader(kmreq, kmresp, key, clusterVersion);

            checkDeviceLocked(kmreq, kmresp);
            // prepare to process this request
            context.preProcessRequest();

            checkBatchMode(kmreq);

            if (kmreq.getIsBatchMessage()) {
                this.processBatchOpMessage(kmreq, kmresp);
                this.processBatchOpMessage(context);
            } else {
                this.manager.getHandler(mtype).processRequest(kmreq, kmresp);
                // process request
                context.processRequest();
            }

        } catch (DeviceLockedException ire) {

            int number = kmreq.getCommand().getHeader().getMessageType()
                    .getNumber() - 1;

            commandBuilder.getHeaderBuilder().setMessageType(
                    MessageType.valueOf(number));

            commandBuilder.getStatusBuilder().setCode(StatusCode.DEVICE_LOCKED);

            commandBuilder.getStatusBuilder().setStatusMessage(
                    "Device is locked");
        } catch (NotAttemptedException nae) {
            // handle not attempted exception
            commandBuilder.getStatusBuilder().setCode(StatusCode.NOT_ATTEMPTED);
            commandBuilder.getStatusBuilder()
            context.getCommandBuilder().getStatusBuilder()
                    .setCode(StatusCode.NOT_ATTEMPTED);
            context.getCommandBuilder().getStatusBuilder()
                    .setStatusMessage(nae.getMessage());
        } catch (InvalidBatchException boe) {
            // handle invalid batch exception
            commandBuilder.getStatusBuilder().setCode(StatusCode.INVALID_BATCH);
            commandBuilder.getStatusBuilder()
            context.getCommandBuilder().getStatusBuilder()
                    .setCode(StatusCode.INVALID_BATCH);
            context.getCommandBuilder().getStatusBuilder()
                    .setStatusMessage(boe.getMessage());
        } catch (Exception e) {

            logger.log(Level.WARNING, e.getMessage(), e);

            int number = kmreq.getCommand().getHeader().getMessageType()
                    .getNumber() - 1;

            commandBuilder.getHeaderBuilder().setMessageType(
                    MessageType.valueOf(number));

            /**
             * reset to default error response code if not set
             */
            if (commandBuilder.getStatusBuilder().getCode() == StatusCode.SUCCESS) {
                commandBuilder.getStatusBuilder().setCode(
            if (context.getCommandBuilder().getStatusBuilder().getCode() == StatusCode.SUCCESS) {
                context.getCommandBuilder().getStatusBuilder()
                        .setCode(
                    StatusCode.INVALID_REQUEST);
            }

            if (commandBuilder.getStatusBuilder().hasStatusMessage() == false) {
                commandBuilder.getStatusBuilder().setStatusMessage(
            if (context.getCommandBuilder().getStatusBuilder()
                    .hasStatusMessage() == false) {
                context.getCommandBuilder().getStatusBuilder()
                        .setStatusMessage(
                        e.getMessage());
            }

@@ -551,57 +502,25 @@ public class SimulatorEngine implements MessageService {

            try {

                if (this.batchOp != null && batchOp.isClosed()) {
                    this.endBatchOperation(kmreq);
                }
                
                // get command byte stirng
                ByteString commandByteString = commandBuilder.build()
                        .toByteString();

                // get command byte[]
                byte[] commandByte = commandByteString.toByteArray();
                checkAndReleaseBatchOperation(kmreq);

                // require Hmac calculation ?
                if (kmreq.getMessage().getAuthType() == AuthType.HMACAUTH) {
                // post process message
                context.postProcessRequest();

                    // calculate hmac
                    ByteString hmac = Hmac.calc(commandByte, key);

                    // set identity
                    messageBuilder.getHmacAuthBuilder().setIdentity(userId);

                    // set hmac
                    messageBuilder.getHmacAuthBuilder().setHmac(hmac);
                }

                // set command bytes
                messageBuilder.setCommandBytes(commandByteString);
            } catch (Exception e2) {
                logger.log(Level.WARNING, e2.getMessage(), e2);
            }

            this.addStatisticCounter(kmreq, kmresp);
        }

        return kmresp;
            this.addStatisticCounter(kmreq, context.getResponseMessage());
        }

    private void checkDeviceLocked(KineticMessage kmreq, KineticMessage kmresp)
            throws DeviceLockedException {

        if (this.deviceLocked == false) {
            return;
        return context.getResponseMessage();
    }

        PinOpType pinOpType = kmreq.getCommand().getBody().getPinOp()
                .getPinOpType();

        if (pinOpType != PinOpType.UNLOCK_PINOP
                && pinOpType != PinOpType.LOCK_PINOP) {
            throw new DeviceLockedException();
    public void checkAndReleaseBatchOperation(KineticMessage request) {
        if (this.batchOp != null && batchOp.isClosed()) {
            this.endBatchOperation(request);
        }

    }

    private void addStatisticCounter(KineticMessage kmreq, KineticMessage kmresp) {
@@ -1039,6 +958,8 @@ public class SimulatorEngine implements MessageService {
    @Override
    public synchronized void endBatchOperation(KineticMessage kmreq) {

        if (this.batchOp != null && batchOp.isClosed()) {

            this.batchOp = null;

            this.isInBatchMode = false;
@@ -1047,6 +968,7 @@ public class SimulatorEngine implements MessageService {

            logger.info("batch op ended/released ...");
        }
    }

    private synchronized void checkBatchMode(KineticMessage kmreq)
            throws InvalidRequestException {
@@ -1082,29 +1004,31 @@ public class SimulatorEngine implements MessageService {
        }
    }

    private void processBatchOpMessage(KineticMessage kmreq,
            KineticMessage kmresp) throws InvalidBatchException,
    public void processBatchOpMessage(RequestContext context)
            throws InvalidBatchException,
            NotAttemptedException {

        MessageType mtype = kmreq.getCommand().getHeader().getMessageType();
        MessageType mtype = context.getMessageType();

        if (mtype == MessageType.START_BATCH
                || mtype == MessageType.ABORT_BATCH) {
            return;
        }

        if (kmreq.getIsFirstBatchMessage()) {
            startBatchOperation(kmreq);
        if (context.getRequestMessage().getIsFirstBatchMessage()) {
            startBatchOperation(context.getRequestMessage());
        }

        // init batch operation
        if (this.batchOp == null) {

            throw new InvalidBatchException(
                    "batch operation is either not started or failed");
        }

        // process batch message
        this.batchOp.handleRequest(kmreq, kmresp);
        this.batchOp.handleRequest(context.getRequestMessage(),
                context.getResponseMessage());
    }

}
+28 −0
Original line number Diff line number Diff line
@@ -204,6 +204,16 @@ public class SimulatorConfiguration extends Properties {
     */
    private static int maxIdentityCount = -1;

    /**
     * max number of commands per batch.
     */
    private static int maxCommandsPerBatch = 15;

    /**
     * max number of outstanding batch requests per drive.
     */
    private static int maxOutstandingBatches = 5;

    /**
     * current simulator version.
     */
@@ -1041,4 +1051,22 @@ public class SimulatorConfiguration extends Properties {
        }
    }

    /**
     * Get maximum number of commands per batch request.
     * 
     * @return maximum number of commands per batch request.
     */
    public int getMaxCommandsPerBatch() {
        return maxCommandsPerBatch;
    }

    /**
     * Get maximum number of outstanding batch requests per device.
     * 
     * @return maximum number of outstanding batch requests per device.
     */
    public int getMaxOutstandingBatches() {
        return maxOutstandingBatches;
    }

}