/** * 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.tcp; import io.netty.handler.codec.CorruptedFrameException; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.util.logging.Level; import java.util.logging.Logger; import com.google.protobuf.InvalidProtocolBufferException; import com.seagate.kinetic.common.lib.KineticMessage; import com.seagate.kinetic.proto.Kinetic.Command; import com.seagate.kinetic.proto.Kinetic.Message; import com.seagate.kinetic.proto.Kinetic.Message.Builder; import com.seagate.kinetic.simulator.internal.MessageHandler; /** * * TCP message service i/o handler. * <p> * * @author James Hughes * @author chiaming Yang * */ public class IoHandler implements Runnable { private final static Logger logger = Logger.getLogger(IoHandler.class .getName()); private boolean useV2Protocol = true; // client paired socket private Socket socket = null; // input stream of the socket private InputStream is = null; // output stream of the socket private OutputStream os = null; // my io service private TcpTransportProvider ioService = null; // message handler. private MessageHandler msgHandler = null; /** * Constructor * * @param s * client paired socket * @param ioService * my io service */ IoHandler(Socket s, TcpTransportProvider ioService) { this.socket = s; this.ioService = ioService; this.useV2Protocol = true; } /** * Get io service associated to this instance. * * @return io service associated to this instance */ public TcpTransportProvider getIoService() { return this.ioService; } /** * Called by MessageHandler to send response message. * * @param out * response message. */ public void sendResponse(KineticMessage out) { try { if (logger.isLoggable(Level.FINEST)) { logger.finest("writing respond message: " + out); } if (this.useV2Protocol) { try { // get value to write separately byte[] value = out.getValue(); // write 9 byte header ByteArrayOutputStream baos = new ByteArrayOutputStream(9); DataOutputStream dos = new DataOutputStream(baos); // magic dos.writeByte((byte) 'F'); // set value to an empty value // out.setValue(ByteString.EMPTY); // build message (without value) to write Message msg = ((Builder) out.getMessage()).build(); // get proto message bytes byte[] protoMessageBytes = msg.toByteArray(); // write message len dos.writeInt(protoMessageBytes.length); // write attached value size, 4 byte int valueLen = 0; if ((value != null)) { valueLen = value.length; } dos.writeInt(valueLen); dos.flush(); // 9 byte header byte[] header = baos.toByteArray(); dos.close(); baos.close(); // 1. write header bytes (9 bytes) os.write(header); // 2. write protobuf message byte[] os.write(protoMessageBytes); // 3 (optional) write attached value if any if (value != null && value.length > 0) { // write value os.write(value); } os.flush(); } catch (IOException e) { logger.log(Level.WARNING, e.getMessage(), e); throw e; } } else { ((Builder) out.getMessage()).build().writeDelimitedTo(os); } } catch (IOException e) { // LOG.severe("Sending Response failed"); e.printStackTrace(); } } @Override public void run() { // Message out = null; DataInputStream dis = null; try { is = socket.getInputStream(); os = socket.getOutputStream(); // LOG.fine("individual connection has streams"); dis = new DataInputStream(is); this.msgHandler = new MessageHandler(this); // This will get individual commands and processes them. // it is either a blocking interface, or not. If process // message returns null, that routine must call // this.sendResponse() // themselves, possibly from anther thread. for (;;) { Message request = null; KineticMessage km = new KineticMessage(); if (this.useV2Protocol) { // 1. Read magic number. int magicNumber = dis.readByte(); if (magicNumber != 'F') { throw new CorruptedFrameException( "Invalid magic number: " + magicNumber); } // 2. protobuf message size int protoMessageLength = dis.readInt(); // 3. attched value size int attachedValueLength = dis.readInt(); // 4. read protobuf message byte[] decoded = new byte[protoMessageLength]; dis.read(decoded); // construct protobuf message Message.Builder builder = Message.newBuilder(); try { builder.mergeFrom(decoded); } catch (Exception e) { logger.log(Level.WARNING, e.getMessage(), e); throw new RuntimeException(e); } // 5. read attched value if any if (attachedValueLength > 0) { // construct byte[] byte[] attachedValue = new byte[attachedValueLength]; // read from buffer dis.read(attachedValue); // set to message // builder.setValue(ByteString.copyFrom(attachedValue)); km.setValue(attachedValue); } // v2 message request = builder.build(); } else { request = Message.parseDelimitedFrom(is); } if (request == null) { break; } // build command Command.Builder commandBuilder = Command.newBuilder(); try { commandBuilder.mergeFrom(request.getCommandBytes()); km.setCommand(commandBuilder.build()); } catch (InvalidProtocolBufferException e) { logger.log(Level.WARNING, e.getMessage(), e); } if (logger.isLoggable(Level.FINEST)) { logger.finest("received request message: " + request); } km.setMessage(request); this.msgHandler.processRequest(km); } } catch (Exception e) { if (e instanceof EOFException) { logger.fine(e.getMessage()); } else { logger.log(Level.WARNING, e.getMessage(), e); } } try { if (this.msgHandler != null) { this.msgHandler.close(); } if (is != null) { is.close(); os.close(); } if (dis != null) { dis.close(); } socket.close(); // LOG.fine("Individual connection closed"); } catch (IOException e) { // LOG.severe("individual connection close failed"); logger.log(Level.WARNING, e.getMessage(), e); } logger.info("IoHandler closed, ioservice=" + this.ioService.getName() + ", use v2 protocol=" + this.useV2Protocol); } }