/* This file is part of VoltDB. * Copyright (C) 2008-2017 VoltDB Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with VoltDB. If not, see <http://www.gnu.org/licenses/>. */ package org.voltcore.network; import java.io.IOException; import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicLong; public abstract class VoltProtocolHandler implements InputHandler { /** VoltProtocolPorts each have a unique id */ private static AtomicLong m_globalConnectionCounter = new AtomicLong(0); /** The distinct exception class allows better logging of these unexpected errors. */ class BadMessageLength extends IOException { private static final long serialVersionUID = 8547352379044459911L; public BadMessageLength(String string) { super(string); } } /** serial number of this VoltPort */ private final long m_connectionId; private int m_nextLength; public VoltProtocolHandler() { m_connectionId = m_globalConnectionCounter.incrementAndGet(); } public static long getNextConnectionId() { return m_globalConnectionCounter.incrementAndGet(); } @Override public ByteBuffer retrieveNextMessage(final NIOReadStream inputStream) throws BadMessageLength { /* * Note that access to the read stream is not synchronized. In this application * the VoltPort will invoke this input handler to interact with the read stream guaranteeing * thread safety. That said the Connection interface does allow other parts of the application * access to the read stream. */ ByteBuffer result = null; if (m_nextLength == 0 && inputStream.dataAvailable() > (Integer.SIZE/8)) { m_nextLength = inputStream.getInt(); if (m_nextLength < 1) { throw new BadMessageLength( "Next message length is " + m_nextLength + " which is less than 1 and is nonsense"); } if (m_nextLength > VoltPort.MAX_MESSAGE_LENGTH) { throw new BadMessageLength( "Next message length is " + m_nextLength + " which is greater then the hard coded " + "max of " + VoltPort.MAX_MESSAGE_LENGTH + ". Break up the work into smaller chunks (2 megabytes is reasonable) " + "and send as multiple messages or stored procedure invocations"); } assert m_nextLength > 0; } if (m_nextLength > 0 && inputStream.dataAvailable() >= m_nextLength) { result = ByteBuffer.allocate(m_nextLength); // Copy read buffers to result, move read buffers back to memory pool inputStream.getBytes(result.array()); m_nextLength = 0; } return result; } @Override public void started(Connection c) { } @Override public void starting(Connection c) { } @Override public void stopped(Connection c) { } @Override public void stopping(Connection c) { } @Override public long connectionId() { return m_connectionId; } protected int getNextMessageLength() { return m_nextLength; } }