ByteArrayHandler.java
/*
** Module : ByteArrayHandler.java
** Abstract : Splits input data stream into chunks
**
** Copyright (c) 2016-2023, Golden Code Development Corporation.
**
** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
** 001 IAS 20160805 Initial version
** 002 IAS 20200729 Process large messages
** 003 IAS 20210329 Re-worked logging configuration
** 004 GBB 20230512 Logging methods replaced by CentralLogger/ConversionStatus.
*/
/*
** 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 may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
**
** Additional terms under GNU Affero GPL version 3 section 7:
**
** Under Section 7 of the GNU Affero GPL version 3, the following additional
** terms apply to the works covered under the License. These additional terms
** are non-permissive additional terms allowed under Section 7 of the GNU
** Affero GPL version 3 and may not be removed by you.
**
** 0. Attribution Requirement.
**
** You must preserve all legal notices or author attributions in the covered
** work or Appropriate Legal Notices displayed by works containing the covered
** work. You may not remove from the covered work any author or developer
** credit already included within the covered work.
**
** 1. No License To Use Trademarks.
**
** This license does not grant any license or rights to use the trademarks
** Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
** of Golden Code Development Corporation. You are not authorized to use the
** name Golden Code, FWD, or the names of any author or contributor, for
** publicity purposes without written authorization.
**
** 2. No Misrepresentation of Affiliation.
**
** You may not represent yourself as Golden Code Development Corporation or FWD.
**
** You may not represent yourself for publicity purposes as associated with
** Golden Code Development Corporation, FWD, or any author or contributor to
** the covered work, without written authorization.
**
** 3. No Misrepresentation of Source or Origin.
**
** You may not represent the covered work as solely your work. All modified
** versions of the covered work must be marked in a reasonable way to make it
** clear that the modified work is not originating from Golden Code Development
** Corporation or FWD. All modified versions must contain the notices of
** attribution required in this license.
*/
package com.goldencode.p2j.net;
import com.goldencode.p2j.util.logging.*;
import java.io.*;
import java.nio.*;
/**
* This class splits input data stream into chunks. Each chunk is a sequence of bytes prepended with an
* integer length.
*/
public abstract class ByteArrayHandler
{
/** Work buffer. */
private volatile ByteBuffer bb;
/** Trace operations' flag */
private final boolean trace;
/** The length of the next chunk. */
private volatile int len = -1;
/** Bytes to be read in a large message */
private volatile int remaining = 0;
/** Large message data holder */
private volatile ByteArrayOutputStream baos = null;
/**
* Constructor
*
* @param bufsize
* max incoming portion size
* @param trace
* trace operations flag
*/
public ByteArrayHandler(int bufsize, boolean trace)
{
super();
this.bb = ByteBuffer.allocate(4*bufsize);
this.trace = trace;
}
/**
* Process the next portion of input
*
* @param chunk
* input data
*
* @return number of chunks found since previous input
*/
public synchronized int nextChunk(ByteBuffer chunk)
{
int parsed = 0;
if (bb.remaining() < chunk.remaining());
{
ByteBuffer nbb = ByteBuffer.allocate(bb.position()+chunk.remaining());
bb.flip();
nbb.put(bb);
bb = nbb;
}
try
{
bb.put(chunk);
}
catch (BufferOverflowException e)
{
CentralLogger.get(ByteArrayHandler.class)
.warning("Exception in processing the next portion of input:", e);
}
while (next())
{
parsed++;
}
return parsed;
}
/**
* Process the byte portion of the next chunk
*
* @param data
* the byte portion of the next chunk
*/
protected abstract void ready(byte[] data);
/**
* Try to extract a next chunk from the input
*
* @return <code>true</code> if a new chunk was found
*/
private boolean next()
{
if (len == -1)
{
if (bb.position() < 4)
{
return false;
}
int pos = bb.position();
bb.flip();
bb.rewind();
len = bb.getInt();
bb.position(pos);
}
if (len < 0)
{
if (trace)
{
NetSocketBase.LOG.finest(
String.format("Long message to be received, length: %d", -len));
}
remaining = -len;
baos = new ByteArrayOutputStream(remaining);
len = -1;
bb.compact();
return false;
}
if (trace && baos != null)
{
NetSocketBase.LOG.finest(
String.format("Next chunk of long message received, length: %d", len));
}
if (bb.position() < len + 4)
{
return false;
}
byte[] data = new byte[len];
bb.position(4);
bb.get(data);
len = -1;
bb.compact();
if (baos != null)
{
try
{
baos.write(data);
}
catch (IOException e) // should never happen
{
throw new RuntimeException("Unexpected exception", e);
}
remaining -= data.length;
if (trace)
{
NetSocketBase.LOG.finest(
String.format("Next chunk of the long message: %d, remaining: %d",
data.length, remaining));
}
if (remaining < 0)
{
throw new RuntimeException("Unexpected value of the 'remaining':" + remaining);
}
if (remaining > 0)
{
return false;
}
if (remaining == 0)
{
try
{
baos.close();
}
catch (IOException e) // should never happen
{
throw new RuntimeException("Unexpected exception", e);
}
data = baos.toByteArray();
baos = null;
if (trace)
{
NetSocketBase.LOG.finest(
String.format("Long message received, len: %d", len));
}
}
}
ready(data);
return true;
}
}