/** * 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.io.provider.nio; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import java.util.logging.Level; import java.util.logging.Logger; 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.Status.StatusCode; import com.seagate.kinetic.proto.Kinetic.Message; import com.seagate.kinetic.proto.Kinetic.Message.AuthType; import com.seagate.kinetic.simulator.internal.ConnectionInfo; import com.seagate.kinetic.simulator.internal.FaultInjectedCloseConnectionException; import com.seagate.kinetic.simulator.internal.SimulatorEngine; import com.seagate.kinetic.simulator.io.provider.spi.MessageService; /** * * @author chiaming */ public class NioMessageServiceHandler extends SimpleChannelInboundHandler<KineticMessage> { private static final Logger logger = Logger .getLogger(NioMessageServiceHandler.class.getName()); private MessageService lcservice = null; private boolean enforceOrdering = false; private NioQueuedRequestProcessRunner queuedRequestProcessRunner = null; private static boolean faultInjectCloseConnection = Boolean .getBoolean(FaultInjectedCloseConnectionException.FAULT_INJECT_CLOSE_CONNECTION); private boolean isSecureChannel = false; public NioMessageServiceHandler(MessageService lcservice2, boolean isSecureChannel) { this.lcservice = lcservice2; this.isSecureChannel = isSecureChannel; this.enforceOrdering = lcservice.getServiceConfiguration() .getMessageOrderingEnforced(); if (this.enforceOrdering) { this.queuedRequestProcessRunner = new NioQueuedRequestProcessRunner( lcservice); } } @Override public void channelActive (ChannelHandlerContext ctx) throws Exception { super.channelActive(ctx); // register connection info with the channel handler context @SuppressWarnings("unused") ConnectionInfo info = this.lcservice.registerNewConnection(ctx); //logger.info("***** connection registered., sent UNSOLICITEDSTATUS with cid = " + info.getConnectionId()); } @Override protected void channelRead0(ChannelHandlerContext ctx, KineticMessage request) throws Exception { if (faultInjectCloseConnection) { KineticMessage response = this .createUnsolicitedStatusMessageWithBuilder(); ctx.writeAndFlush(response); throw new FaultInjectedCloseConnectionException( "Fault injected for the simulator"); } // set ssl channel flag to false request.setIsSecureChannel(isSecureChannel); // check if conn id is set NioConnectionStateManager.checkIfConnectionIdSet(ctx, request); boolean shouldContinue = NioBatchOpPreProcessor.processMessage(this, ctx, request); if (shouldContinue) { // process regular request processRequest(ctx, request); } } public void processRequest(ChannelHandlerContext ctx, KineticMessage request) throws InterruptedException { if (enforceOrdering) { if (this.shouldProcessRequestAsync(request)) { // process request async this.processRequestAsync(ctx, request); } else { // process request sequentially queuedRequestProcessRunner.processRequest(ctx, request); } } else { this.processRequestAsync(ctx, request); } } /** * Process request asynchronously. The calling thread does not wait for the * request to be processed and returns immediately. * * @param ctx * @param request * @throws InterruptedException */ private void processRequestAsync(ChannelHandlerContext ctx, KineticMessage request) throws InterruptedException { // each request is independently processed RequestProcessRunner rpr = null; rpr = new RequestProcessRunner(lcservice, ctx, request); this.lcservice.execute(rpr); logger.info("***** request processed asynchronously ...."); } private boolean shouldProcessRequestAsync(KineticMessage request) { boolean flag = false; MessageType mtype = request.getCommand().getHeader().getMessageType(); if (mtype == MessageType.MEDIAOPTIMIZE || mtype == MessageType.MEDIASCAN) { flag = true; } return flag; } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { logger.log(Level.WARNING, "Unexpected exception from downstream.", cause); // close process runner if (this.queuedRequestProcessRunner != null) { this.queuedRequestProcessRunner.close(); } // close context ctx.close(); } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { // remove connection info of the channel handler context from conn info // map ConnectionInfo info = SimulatorEngine.removeConnectionInfo(ctx); // remove batches in batch queue NioBatchOpPreProcessor.cleanUpConnection(info.getConnectionId()); logger.info("connection info is removed, id=" + info.getConnectionId() + ", is secure channel=" + this.isSecureChannel); } public MessageService getMessageService() { return this.lcservice; } /** * Create an internal message with empty builder message. * * @return an internal message with empty builder message */ public static KineticMessage createUnsolicitedStatusMessageWithBuilder() { // 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.UNSOLICITEDSTATUS); // create command builder Command.Builder commandBuilder = Command.newBuilder(); commandBuilder.getStatusBuilder().setCode( StatusCode.CONNECTION_TERMINATED); commandBuilder.getStatusBuilder().setDetailedMessage( ByteString.copyFromUtf8("Connection closed")); // get command byte stirng ByteString commandByteString = commandBuilder.build().toByteString(); message.setCommandBytes(commandByteString); // set command kineticMessage.setCommand(commandBuilder); return kineticMessage; } }