src.com.ibm.as400.access.DBBaseRequestDS Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jt400-jdk8 Show documentation
Show all versions of jt400-jdk8 Show documentation
The Open Source version of the IBM Toolbox for Java
///////////////////////////////////////////////////////////////////////////////
//
// JTOpen (IBM Toolbox for Java - OSS version)
//
// Filename: DBBaseRequestDS.java
//
// The source code contained herein is licensed under the IBM Public License
// Version 1.0, which has been approved by the Open Source Initiative.
// Copyright (C) 1997-2003 International Business Machines Corporation and
// others. All rights reserved.
//
///////////////////////////////////////////////////////////////////////////////
package com.ibm.as400.access;
import java.io.ByteArrayOutputStream; //@PDA for lazy close check
import java.io.CharConversionException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Enumeration;
import java.util.Hashtable;
import java.sql.SQLException;
//--------------------------------------------------------------------
//
// Each request consists of 3 parts: Header, Template, and
// Optional / Variable-length Data
//
// Header: Bytes 1 - 4: Length of the request
// 5 - 6: Header id
// 7 - 8: Client/Server id
// 9 - 12: Client/Server instance
// 13 - 16: Correlation id
// 17 - 18: Length of template
// 19 - 20: Request id
//
// Template: This is fixed length data that is required.
// Each request has it's own template format and may vary
// depending on both the request id and client/server id.
// The operational results are stored on the IBM i
// system and can be received at a later time.
// Bitmap: Used to identify the operation results to
// return. Bit 1 is the left-most bit when it
// arrives at the IBM i system
// Bit 1: 1=reply should be sent immediately to
// the client application
// 0=reply is not sent and the rest of
// the bit map is ignored
// Bit 2: Message Id
// Bit 3: First Level Text
// Bit 4: Second Level Text
// Bit 5: Data Format
// Bit 6: Result Data
// Bit 7: SQLCA SQL Communications Area
// Bit 8: Server Attributes
// Bit 9: Parameter Marker format
// Bit 10: Translation Tables
// Bit 11: Data Source Information
// Bit 12: Package Information
// Bit 13: Request is RLE compressed @E3A
// Bit 14: RLE compression reply desired @E3A
// Bit 15: Extended column descriptors @F1C
// Bit 16: Varying Length Column Compression @K54
// Bit 17-32: Reserved @E3C @F1C @K54
// Reserved Area:
// RTNORS: Numeric value of the Operation Results Set
// (ORS) that contains the data to be returned
// to the client.
// It must be non-negative.
// FILLORS: Numeric value of the ORS used to store the
// data for this function.
// It must be non-negative.
// BONORS: Numeric value of the "based-on" ORS.
// If used, it must be positive. If it is not
// positive, it will be ignored.
// RPBHNDL: Numeric value of the Request Parameter Block
// (RPB) to be used.
// This may be set to zero indicating that an RPB
// will not be used.
// PM DESCHNDL: Parameter marker descriptor handle
// identifier.
// This field is only used when the serverId
// is "E004"X (SQL).
// PARMCNT: Number of parameters in the optional/variable-
// length data.
//
// Optional / Variable-length Data:
// This is optional data and/or mandatory data that
// varies in length.
// o Use format LL CP data
// LL = length
// CP = code point
// o Character data is optionally preceede by a
// coded character set of the data (CCSID)
// o The CCSID can identify the code page and the
// character set of the data
// o All LLs include the length of the LL
// o The client is responsible for providing numerics
// in the server format so the system will not
// need to perform data conversions.
//
//---------------------------------------------------------------------
/**
This is the base class for all database server request data
streams. Every concrete database server request data stream
should inherit from this class.
Here are the steps needed to send a request data stream to the
server:
1. For all such requests, construct the data stream object,
passing the required parameters.
2. The header and template portions of the data stream will
be created by the constructor.
3. Use the addParameter function to add any additional
parameters required by the specific function. These
parameters will be part of the Optional / Variable-length
section of the header.
4. After setting all the parameters, but before sending the
request, invoke the dataStreamReady function.
This function will:
- Clean up the data stream
- Set values that can not be calculated until all
all parameters are set
- Request length
- parameter count
5. Then you are ready to send the request to the system. There are
two choices:
- If no reply is needed use:
try {
server.send (request);
}
catch {
// Handle the exception.
}
- If a reply is needed use:
DataStream reply = null;
try {
reply = server.sendAndReceive (request);
}
catch (Exception e) {
// Handle the exception.
}
@See com.ibm.as400.access.DBReturnRequestedDataDS
@See com.ibm.as400.access.AS400Server
**/
//-----------------------------------------------------//
// //
// addParameter is used to add an optional / variable-//
// length parameter to the datastream. After all //
// the parameters are added, dataStreamReady should //
// be called and then the data stream will be done. //
// //
// The addParameter method is used to handle the //
// following types of parameters: //
// Request Function Parameters //
// Attribute Function Parameters //
// Descriptor Function Parameters //
// Result Set Function Parameters //
// RPB Function Parameters //
// //
// The addParameter used will depend on the format, //
// not the type. //
// //
// Each addParameter will do the following: //
// 1. Test to make sure there is room in the //
// data stream. //
// 2. Put the length (LL) into the data stream. //
// Include the 4 bytes for the LL in the length. //
// 3. Put the code point (parameter id) into the //
// the data stream. //
// 4. Put the data into the data stream. //
// 5. Add the length to the total request length. //
// 6. Add to parameter count. //
// //
//-----------------------------------------------------//
abstract class DBBaseRequestDS
extends ClientAccessDataStream
{
public static int SEND_HISTORY_SIZE=10; /*@A8A*/
// Private data.
private int currentOffset_;
private int lockedLength_;
private int operationResultBitmap_;
private int parameterCount_;
private boolean rleCompressed_ = false; // @E3A
private final DBStorage storage_ = DBDSPool.storagePool_.getUnusedStorage(); //@P0A
private int[] sendHistory = new int[SEND_HISTORY_SIZE]; // @A8A
private int sendHistoryOffset = 0; // @A8A
// Values for operation result bitmap.
public static final int ORS_BITMAP_RETURN_DATA = 0x80000000; // Bit 1
public static final int ORS_BITMAP_MESSAGE_ID = 0x40000000; // Bit 2
public static final int ORS_BITMAP_FIRST_LEVEL_TEXT = 0x20000000; // Bit 3
public static final int ORS_BITMAP_SECOND_LEVEL_TEXT = 0x10000000; // Bit 4
public static final int ORS_BITMAP_DATA_FORMAT = 0x08000000; // Bit 5
public static final int ORS_BITMAP_RESULT_DATA = 0x04000000; // Bit 6
public static final int ORS_BITMAP_SQLCA = 0x02000000; // Bit 7
public static final int ORS_BITMAP_SERVER_ATTRIBUTES = 0x01000000; // Bit 8
public static final int ORS_BITMAP_PARAMETER_MARKER_FORMAT = 0x00800000; // Bit 9
// public static final int ORS_BITMAP_TRANSLATION_TABLES = 0x00400000; // Bit 10
// public static final int ORS_BITMAP_DATA_SOURCE_INFORMATION = 0x00200000; // Bit 11
public static final int ORS_BITMAP_PACKAGE_INFO = 0x00100000; // Bit 12
public static final int ORS_BITMAP_REQUEST_RLE_COMPRESSION = 0x00080000; // Bit 13 @E3A
public static final int ORS_BITMAP_REPLY_RLE_COMPRESSION = 0x00040000; // Bit 14 @E3A
public static final int ORS_BITMAP_EXTENDED_COLUMN_DESCRIPTORS = 0x00020000; // Bit 15 @F1A
public static final int ORS_BITMAP_VARIABLE_LENGTH_FIELD_COMPRESSION = 0x00010000; //Bit 16 @K54
public static final int ORS_BITMAP_CURSOR_ATTRIBUTES = 0x00008000; //Bit 17 @CUR
// Server IDs.
protected static final int SERVER_SQL = 0xE004;
protected static final int SERVER_NDB = 0xE005;
protected static final int SERVER_ROI = 0xE006;
// This is the length that data must be to be considered for RLE compression. @E3A
// It is currently set to 1024 + 40 (header and template + 1K). It must be @E3A
// set to at least 40. @E3A
private static final int RLE_THRESHOLD_ = 1064; // @E3A
/**
Constructor.
**/
protected DBBaseRequestDS(int requestId,
int rpbId,
int operationResultBitmap,
int parameterMarkerDescriptorHandle)
{
super();
initialize(requestId, rpbId, operationResultBitmap, parameterMarkerDescriptorHandle); //@P0A
}
//@P0A - This code used to be in the constructor.
// Now, just call initialize() instead of constructing a new datastream.
void initialize(int requestId,
int rpbId,
int operationResultBitmap,
int parameterMarkerDescriptorHandle)
{
// Allocate the large byte array for storage of the
// data stream.
data_ = storage_.getData(); //@P0C
// Initialization.
currentOffset_ = HEADER_LENGTH + 20;
parameterCount_ = 0;
operationResultBitmap_ = operationResultBitmap;
rleCompressed_ = false; //@P0A
lockedLength_ = 0; //@P0A
// Data stream header.
setHeaderID(0);
setCSInstance(0);
setTemplateLen(20);
setReqRepID(requestId);
// Data stream template.
set32bit(operationResultBitmap, 20); // Operation result bitmap.
set16bit(rpbId, 28); // Return ORS handle.
set16bit(rpbId, 30); // Fill ORS handle.
setBasedOnORSHandle(0); // Based on ORS handle.
set16bit(rpbId, 34); // RPB handle.
setParameterMarkerDescriptorHandle(parameterMarkerDescriptorHandle);
}
/**
Adds another operation result to the operation result bitmap.
* @param value
**/
public void addOperationResultBitmap(int value)
{
operationResultBitmap_ |= value;
set32bit(operationResultBitmap_, 20);
}
/**
Adds a 1 byte parameter.
**/
protected void addParameter(int codePoint, byte value) throws DBDataStreamException
{
lock(1, codePoint);
data_[currentOffset_] = value;
unlock();
}
/**
Adds a 2 byte parameter.
**/
protected void addParameter(int codePoint, short value)
throws DBDataStreamException
{
lock(2, codePoint);
set16bit(value, currentOffset_);
unlock();
}
/**
Adds a 2 byte parameter with an extra 4 byte value.
**/
protected void addParameter(int codePoint, short value, int extra)
throws DBDataStreamException
{
lock(6, codePoint);
set16bit(value, currentOffset_);
set32bit(extra, currentOffset_ + 2);
unlock();
}
/**
Adds a 4 byte parameter.
**/
protected void addParameter(int codePoint, int value)
throws DBDataStreamException
{
lock(4, codePoint);
set32bit(value, currentOffset_);
unlock();
}
/**
Adds a byte array parameter.
**/
protected void addParameter(int codePoint, byte[] value)
throws DBDataStreamException
{
if(value == null){ //@eWLM Can pass a null value in
//"Locks" the request datastream for addition of a parameter. This will determine if there is space left in the data byte array and grow it as needed.
lock(0, codePoint); //@eWLM Locks the datastream and adds the codepoint to it
unlock(); //@eWLM "Unlocks" the datastream
if(Trace.traceOn_) Trace.log(Trace.DIAGNOSTIC, "Value is null, sending only length and codepoint."); //@eWLM
} //@eWLM
else //@eWLM
addParameter(codePoint, value, 0, value.length);
}
protected void addParameter(int codePoint, byte[] value, int offset, int length) throws DBDataStreamException
{
lock(length, codePoint);
System.arraycopy(value, offset, data_, currentOffset_, length);
unlock();
}
/**
@B2A Adds a byte array parameter including CCSID and length.
**/
protected void addParameter(int codePoint,
byte[] value,
boolean overloadThisMethod)
throws DBDataStreamException
{
addParameter(codePoint, value, 0, value.length, overloadThisMethod);
}
protected void addParameter(int codePoint, byte[] value, int offset, int length, boolean overloadThisMethod) throws DBDataStreamException
{
lock(value.length + 6, codePoint);
set16bit((short)0xFFFF, currentOffset_);
set32bit(length, currentOffset_ + 2);
System.arraycopy(value, offset, data_, currentOffset_ + 6, length);
unlock();
}
// @E4A
/**
Adds a fixed length string parameter which contains only numbers.
This assumption avoids character conversion.
**/
protected void addParameter(int codePoint, String value) // @E4A
throws DBDataStreamException // @E4A
{ // @E4A
char[] asChars = value.toCharArray(); // @E4A
lock(asChars.length + 2, codePoint); // @E4A
set16bit(37, currentOffset_); // CCSID @E4A
int offset = currentOffset_ + 2; // @E4A
for (int i = 0; i < asChars.length; ++i, ++offset)
{ // @E4A
if (asChars[i] == ' ') // @E4A
data_[offset] = 0x40; // @E4A
else // @E4A
data_[offset] = (byte)(asChars[i] | 0x00F0); // @E4A
} // @E4A
unlock(); // @E4A
} // @E4A
/**
Adds a fixed length string parameter.
**/
//
// This does not need to work with double byte character sets
// as far as I know.
//
protected void addParameter(int codePoint,
ConvTable converter, //@P0C
String value,
int valueLength)
throws DBDataStreamException
{
lock(valueLength + 2, codePoint);
set16bit(converter.ccsid_, currentOffset_); // CCSID @P0C
try
{
converter.stringToByteArray(value.substring (0, valueLength),
data_, currentOffset_ + 2);
}
catch (CharConversionException e)
{
throw new DBDataStreamException();
}
unlock();
}
/**
Adds a variable length string parameter.
**/
protected void addParameter(int codePoint,
ConvTable converter, //@P0C
String value)
throws DBDataStreamException, SQLException // @E9a
{
// @A1C
// Changed code to use the converter to find out the exact
// number of bytes the string needs to occupy so that it works
// for both single-byte and double-byte strings.
byte[] rawBytes = converter.stringToByteArray(value);
if (rawBytes.length > 65535) // @E9a
JDError.throwSQLException (JDError.EXC_SQL_STATEMENT_TOO_LONG); // @E9a
lock(rawBytes.length + 4, codePoint);
set16bit(converter.ccsid_, currentOffset_); // CCSID @P0C
set16bit(rawBytes.length, currentOffset_ + 2); // SL
try
{
System.arraycopy(rawBytes, 0, data_, currentOffset_ + 4,
rawBytes.length);
}
catch (Exception e)
{
throw new DBDataStreamException();
}
unlock();
}
//@540
/**
Adds a variable length string parameter with a four byte length.
**/
//Note: This method will only be called if running to a V5R4 or later system. boolean v5r4 is just to distinguish this
// method from the method above (protected void addParameter(int codePoint,ConvTable converter, String value)).
protected void addParameter(int codePoint, boolean v5r4,
ConvTable converter,
String value)
throws DBDataStreamException, SQLException
{
byte[] rawBytes = converter.stringToByteArray(value);
if (rawBytes.length > 2097152) //CHECK TO SEE IF GREATER THAN 2MB
JDError.throwSQLException (JDError.EXC_SQL_STATEMENT_TOO_LONG);
lock(rawBytes.length + 6, codePoint);
set16bit(converter.ccsid_, currentOffset_); // CCSID
set32bit(rawBytes.length, currentOffset_ + 2); // SL - Set 4-byte length
try
{
System.arraycopy(rawBytes, 0, data_, currentOffset_ + 6,
rawBytes.length);
}
catch (Exception e)
{
throw new DBDataStreamException();
}
unlock();
}
/**
Adds a fixed length string parameter, but uses character conversion.
**/
protected void addParameter(int codePoint,
ConvTable converter, //@P0C
String value,
boolean fixed)
throws DBDataStreamException, SQLException // @E9a
{
if(fixed){
// Changed code to use the converter to find out the exact
// number of bytes the string needs to occupy so that it works
// for both single-byte and double-byte strings.
byte[] rawBytes = converter.stringToByteArray(value);
if (rawBytes.length > 65535)
JDError.throwSQLException (JDError.EXC_SQL_STATEMENT_TOO_LONG);
lock(rawBytes.length + 4, codePoint);
set16bit(converter.ccsid_, currentOffset_);
try
{
System.arraycopy(rawBytes, 0, data_, currentOffset_ + 2,
rawBytes.length);
}
catch (Exception e)
{
throw new DBDataStreamException();
}
unlock();
}
else
addParameter(codePoint, converter, value);
}
/**
Adds an empty code point
**/
protected void addParameter(int codePoint)
throws DBDataStreamException
{
lock(0, codePoint);
unlock();
}
/**
Adds a library list parameter.
**/
protected void addParameter(int codePoint,
ConvTable converter, //@P0C
char[] indicators,
String[] libraries)
throws DBDataStreamException
{
int parameterLength = 4;
for (int i = 0; i < libraries.length; ++i)
parameterLength += 3 + libraries[i].length();
lock(parameterLength, codePoint);
set16bit(converter.ccsid_, currentOffset_); // CCSID @P0C
set16bit(libraries.length, currentOffset_ + 2); // number of libraries
int offset = 4;
try
{
for (int i = 0; i < libraries.length; ++i)
{
Character ch = new Character(indicators[i]);
converter.stringToByteArray(ch.toString(), data_, currentOffset_ + offset);
set16bit(libraries[i].length(), currentOffset_ + offset + 1);
converter.stringToByteArray(libraries[i], data_, currentOffset_ + offset + 3);
offset += 3 + libraries[i].length();
}
}
catch (CharConversionException e)
{
throw new DBDataStreamException();
}
unlock();
}
/**
Adds a NLSS indicator parameter.
**/
protected void addParameter(int codePoint,
ConvTable converter, //@P0C
int type,
String tableFile,
String tableLibrary,
String languageId)
throws DBDataStreamException
{
int parameterLength;
switch (type)
{
case 0:
default:
parameterLength = 2;
break;
case 1:
case 2:
parameterLength = 7;
break;
case 3:
parameterLength = 8 + tableFile.length() + tableLibrary.length();
break;
}
lock(parameterLength, codePoint);
set16bit(type, currentOffset_); // sort value
try
{
switch (type)
{
case 0:
default:
break;
case 1:
case 2:
set16bit(converter.ccsid_, currentOffset_ + 2); // CCSID @P0C
converter.stringToByteArray(languageId, data_,
currentOffset_ + 4); // sort language id
break;
case 3:
set16bit(converter.ccsid_, currentOffset_ + 2); // CCSID @P0C
set16bit(tableFile.length(),
currentOffset_ + 4); // SL
converter.stringToByteArray(tableFile, data_,
currentOffset_ + 6); // sort table file
set16bit(tableLibrary.length(),
currentOffset_ + 6 + tableFile.length()); // SL
converter.stringToByteArray(tableLibrary, data_,
currentOffset_ + 8 + tableFile.length()); // sort table library
break;
}
}
catch (CharConversionException e)
{
throw new DBDataStreamException();
}
unlock();
}
/**
Adds a DBOverlay parameter.
**/
protected void addParameter(int codePoint,
DBOverlay value)
throws DBDataStreamException
{
lock(value.getLength(), codePoint);
value.overlay(data_, currentOffset_);
unlock();
}
/**
Adds a DBOverlay parameter and return the offset of
where the parameter length should be updated.
**/
/* @K3A*/
protected int addParameterReserve(int codePoint,
DBOverlay value)
throws DBDataStreamException
{
int offset = lockReserve(value.getLength(), codePoint);
value.overlay(data_, currentOffset_);
unlock();
return offset;
}
/**
Clears an operation result from the operation result bitmap.
* @param value
**/
public void clearOperationResultBitmap(int value) // @E3A
{ // @E3A
if ((operationResultBitmap_ & value) != 0)
{ // @E3A
operationResultBitmap_ ^= value; // @E3A
set32bit(operationResultBitmap_, 20); // @E3A
} // @E3A
} // @E3A
public void compress() // @E3A
{ // @E3A
rleCompressed_ = true; // @E3A
} // @E3A
/**
Output the byte stream contents to the specified PrintStream.
The output format is two hex digits per byte, one space every
four bytes, and sixteen bytes per line.
@param ps the output stream
**/
//
// We need to override the implementation from the super class, since it
// depends on the length of the byte array. We reuse long byte arrays,
// and store the length in a separate variable.
//
// If we did not override this, the dump of a single request would show
// a bunch of extra zero bytes.
//
// In addition, we take the opportunity to also print
// limited character output.
//
void dump(PrintStream ps)
{
dump(ps, data_, currentOffset_);
// Report whether or not the datastream was compressed. @E3A
if (rleCompressed_) // @E3A
ps.println("Request was sent RLE compressed."); // @E3A
}
/**
Output the byte stream contents to the specified PrintStream.
The output format is two hex digits per byte, one space every
four bytes, and sixteen bytes per line.
@param ps the output stream
@param data the data
@param length the length
**/
static void dump(PrintStream ps, byte[] data, int length)
{
synchronized(ps)
{ // @E1A
StringBuffer hexBuffer = new StringBuffer();
StringBuffer charBuffer = new StringBuffer();
int i;
for (i = 0; i < length; i++)
{
// Convert the data to 2 digits of hex.
String temp = "00" + Integer.toHexString(data[i]);
String hex = temp.substring(temp.length() - 2);
hexBuffer.append(hex.toUpperCase());
// Pad hex output at every 4 bytes.
if (i % 4 == 3)
hexBuffer.append(" ");
// Convert the data to an ASCII character.
short ascii = (short) ((data[i] >= 0)
? data[i] : 256 + data[i]);
char ch;
if ((ascii >= 0x81) && (ascii <= 0x89))
ch = (char) ('a' + ascii - 0x81);
else if ((ascii >= 0x91) && (ascii <= 0x99))
ch = (char) ('j' + ascii - 0x91);
else if ((ascii >= 0xA2) && (ascii <= 0xA9))
ch = (char) ('s' + ascii - 0xA2);
else if ((ascii >= 0xC1) && (ascii <= 0xC9))
ch = (char) ('A' + ascii - 0xC1);
else if ((ascii >= 0xD1) && (ascii <= 0xD9))
ch = (char) ('J' + ascii - 0xD1);
else if ((ascii >= 0xE2) && (ascii <= 0xE9))
ch = (char) ('S' + ascii - 0xE2);
else if ((ascii >= 0xF0) && (ascii <= 0xF9))
ch = (char) ('0' + ascii - 0xF0);
else
{ // @E1C
// If we are here, it means that the EBCDIC to ASCII // @E1A
// conversion resulted in an ASCII character that does // @E1A
// not make sense. Lets try it as a Unicode character // @E1A
// straight up. // @E1A
if (data[i] == 0x40) // This could be either Unicode '@' // @E1A
ch = ' '; // or EBCDIC ' '. We will assume the // @E1A
// latter for dumps. // @E1A
else if ((data[i] >= 0x20) && (data[i] <= 0x7E)) // @E1A
ch = (char) data[i]; // @E1A
else // @E1A
ch = '.';
} // @E1A
charBuffer.append(ch);
// Start a new line at every 16 bytes.
if (i % 16 == 15)
{
ps.println(hexBuffer + " [" + charBuffer + "]");
hexBuffer = new StringBuffer();
charBuffer = new StringBuffer();
}
}
// Pad out and print the last line if necessary.
if (i % 16 != 0)
{
int hexBufferLength = hexBuffer.length();
for (int j = hexBufferLength; j <= 35; ++j)
hexBuffer.append(" ");
ps.println(hexBuffer + " [" + charBuffer + "]");
}
} // @E1A
}
public int getOperationResultBitmap() // @E2A
{ // @E2A
return operationResultBitmap_; // @E2A
} // @E2A
// @E7a new method.
// Make sure the buffer is free before going away.
protected void finalize()
throws Throwable
{
//@P0D freeCommunicationsBuffer();
if (storage_ != null) {
storage_.returnToPool(); /* storage_ = null; */ //@P0A
}
data_ = null; //@P0A
super.finalize();
}
/**
"Locks" the request datastream for addition of a parameter.
This will determine if there is space left in the data
byte array and grow it as needed.
@param length The length to be added to the data stream,
in bytes, not including the LL and CP.
**/
private void lock(int length, int codePoint)
throws DBDataStreamException
{
if (storage_.checkSize(currentOffset_ + length + 6))
data_ = storage_.getData(); //@P0C
lockedLength_ = length;
set32bit(length + 6, currentOffset_); // LL
set16bit(codePoint, currentOffset_ + 4); // CP
currentOffset_ += 6;
}
/**
"LocksReserve" the request datastream for addition of a parameter.
This will determine if there is space left in the data
byte array and grow it as needed.
This returns the offset of where the length should be updated
via a call to request.updateLength()
@param length The length to be added to the data stream,
in bytes, not including the LL and CP.
**/
/*@K3A*/
private int lockReserve(int length, int codePoint)
throws DBDataStreamException
{
int lengthOffset;
if (storage_.checkSize(currentOffset_ + length + 6))
data_ = storage_.getData(); //@P0C
lockedLength_ = length;
lengthOffset = currentOffset_;
set32bit(length + 6, currentOffset_); // LL
set16bit(codePoint, currentOffset_ + 4); // CP
currentOffset_ += 6;
return lengthOffset;
}
/** update the length of the block using the previously saved
* offset. Add 6 to the length to include the 6 bytes at the
* beginning
* @param offset
* @param length
*/
public void updateLength(int offset, int length) {
set32bit(length + 6, offset);
currentOffset_ = currentOffset_ - lockedLength_ + length; // TODO: Check this..
}
/**
Sets the numeric value of the "based-on" Operation Results
Set (ORS) into the request datastream.
@param value The numeric value of the based-on ORS.
**/
//------------------------------------------------------------
// The based-on ORS handle specifies an ORS from a previous
// operation. It is used to be able to "chain" requests
// together without checking the results or the previous
// request. If the previous request (whose results are stored
// in the based-on ORS) failed, then this request will not be
// executed.
//
// The based-on ORS handle should be 0 for the first request
// in a chain.
//-----------------------------------------------------------
public void setBasedOnORSHandle(int value)
{
set16bit(value, 32);
}
/**
"Unlocks" the request datastream after addition of a parameter.
**/
private void unlock()
{
currentOffset_ += lockedLength_;
++parameterCount_;
}
/**
Sets the parameter marker descriptor handle.
* @param value
**/
public void setParameterMarkerDescriptorHandle(int value)
{
set16bit(value, 36);
}
/**
Overrides the superclass to write the datastream.
**/
void write(OutputStream out)
throws IOException
{
// record the length of this packet for each send @A8A
sendHistory[sendHistoryOffset] = currentOffset_;
sendHistoryOffset = (sendHistoryOffset + 1) % SEND_HISTORY_SIZE;
setLength(currentOffset_);
set16bit(parameterCount_, 38);
if (rleCompressed_)
{ // @E3A
// Check to see if it is worth doing compression. @E3A
if (currentOffset_ > RLE_THRESHOLD_)
{ // @E3A
// Get another piece of storage from the pool. @E3A
DBStorage secondaryStorage = DBDSPool.storagePool_.getUnusedStorage(); // @E3A @P0C
try //@P0A
{
secondaryStorage.checkSize(currentOffset_); // @E3A
byte[] compressedBytes = secondaryStorage.getData(); // @E3A @P0C
// Compress the bytes not including the header (20 bytes) and template @E3A
// (20 bytes). If the compression was successful, send the compressed @E3A
// bytes. Otherwise, send the bytes as normal. @E3A
//
// The format is this: @E3A
// Bytes: Description: @E3A
// 4 LL - Compressed length of the entire datastream. @E3A
// 36 The rest of the uncompressed header and template. @E3A
// 4 ll - Length of the compressed data + 10. @E5A
// 2 CP - The compression code point. @E5A
// 4 Decompressed length of the data. @E3A
// ll-10 Compressed data. @E3A @E5C
int dataLength = currentOffset_ - 40; // @E3A
int compressedSize = DataStreamCompression.compressRLE(data_, 40, // @E3A
dataLength, compressedBytes, 50, // @E3A @E5C
DataStreamCompression.DEFAULT_ESCAPE); // @E3A
boolean useCompression;
if (compressedSize > 0) {
useCompression = true;
// If the data is LARGE and we do not have a great benefit from compression,
// then do not compress the data. This will save server cycles, but
// we have already spend the cycles on the client.
// Note: There is another check that states if VFC has compressed enough then RLL compression
// will not be used. @L9A
int savingsLength = dataLength - compressedSize;
long savingsPercentage = (100L * savingsLength) / dataLength;
if ((savingsPercentage < 10 ) || (savingsLength < 512)) {
useCompression = false;
}
} else {
useCompression = false;
}
if (useCompression)
{ // @E3A
int compressedSizeWithHeader = compressedSize + 50; // @E3A @E5C
BinaryConverter.intToByteArray(compressedSizeWithHeader, compressedBytes, 0); // @E3A
System.arraycopy(data_, 4, compressedBytes, 4, 36); // @E3A
BinaryConverter.intToByteArray(compressedSize + 10, compressedBytes, 40); // @E5A
BinaryConverter.shortToByteArray((short)AS400JDBCConnectionImpl.DATA_COMPRESSION_RLE_, compressedBytes, 44); // @E5A
BinaryConverter.intToByteArray(dataLength, compressedBytes, 46); // @E3A @E5C
// Synchronization is added around the socket @E3A
// write so that requests from multiple threads @E3A
// that use the same socket won't be garbled. @E3A
synchronized(out)
{ // @E3A
out.write(compressedBytes, 0, compressedSizeWithHeader); // @E3A
out.flush(); // @W1A
}
if (Trace.traceOn_) Trace.log(Trace.DATASTREAM, "Data stream sent (connID="+connectionID_+") ...", compressedBytes, 0, compressedSizeWithHeader); //@E6A @P0C
} // @E3A
else
{ // @E3A
rleCompressed_ = false; // Compression failed. // @E3A
} // @E3A
}
finally //@P0A
{
secondaryStorage.returnToPool(); secondaryStorage = null; //@P0A
}
} // @E3A
else
{ // @E3A
rleCompressed_ = false; // Compression is not worth it. // @E3A
} // @E3A
} // @E3A
if (!rleCompressed_)
{ // @E3A
// The compression was not successful, send the request uncompressed @E3A
// (but still ask for the reply to be compressed). @E3A
clearOperationResultBitmap(ORS_BITMAP_REQUEST_RLE_COMPRESSION); // @E3A
// @A0A
// Synchronization is added around the socket
// write so that requests from multiple threads
// that use the same socket won't be garbled.
synchronized(out)
{
out.write(data_, 0, currentOffset_);
out.flush(); //@W1a
}
//@PDA only trace if stream is actually being sent now. (no trace on lazy close here)
if (Trace.traceOn_ && !(out instanceof ByteArrayOutputStream)) Trace.log(Trace.DATASTREAM, "Data stream sent (connID="+connectionID_+") ...", data_, 0, currentOffset_); //@E6A @P0C
} // @E3A
}
//
// Indicate that the buffer can be returned to the pool. In the past, the pooling implementation
// just set inUse_=false to return to the pool. This is provided so that the request buffer can be resized.
//
synchronized void returnToPool() { // @A8A
// Determine the maximum size of the most recent X request.
// Resize the data.
//
int length = DBStorage.DEFAULT_SIZE;
for (int i = 0 ; i < SEND_HISTORY_SIZE; i++ ) {
if (sendHistory[i] > length) length = sendHistory[i];
}
// System.out.println("Reclaiming when returning to pool");
storage_.reclaim(length);
data_ = null; //Safe, this is assigned during initialize.
super.returnToPool();
}
//
// reclaim the storage
//
void reclaim() {
// System.out.println("Start resetting history");
int defaultSize = DBStorage.DEFAULT_SIZE;
for (int i = 0 ; i < SEND_HISTORY_SIZE; i++ ) {
sendHistory[i] = defaultSize;
}
storage_.reclaim(defaultSize);
// System.out.println("Done resetting history");
data_ = null; //Safe, this is assigned during initialize.
}
}