src.com.ibm.as400.access.AS400FileImplNative 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: AS400FileImplNative.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-2004 International Business Machines Corporation and
// others. All rights reserved.
//
///////////////////////////////////////////////////////////////////////////////
//
// @A3 - 10/30/2007 - The NULL Field Byte Map array must be set based on the
// maximum number of fields for any format in a given file. Refer to
// more detailed explanation for related changes in
// DDMObjectDataStream.java (@A1)
// There is also a related change in AS400FileImplRemote.java
//
///////////////////////////////////////////////////////////////////////////////
package com.ibm.as400.access;
import java.beans.PropertyVetoException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal; //@C1A
import java.util.Vector; //@C1A
//@C0C: We now extend AS400FileImplBase.
class AS400FileImplNative extends AS400FileImplBase
{
private static final String CLASSNAME = "com.ibm.as400.access.AS400FileImplNative";
static
{
if (Trace.traceOn_) Trace.logLoadPath(CLASSNAME);
}
// File handle.
transient int handle_;
// Static synchronization variable for command execution
static String synch_execute_ = "";
// Static synchronization variable for open/close of files.
// The qyjsprl.C smalltalk C module keeps track of the handles allocated
// and freed for files globally. Therefore we synchronize opens and closes
// at the class level for processes using RLA
static Object synch_open_close_ = new Object() ;
static
{
NativeMethods.loadNativeLibraryQyjspart();
try
{
resetStaticStorage(); //@E3A
}
catch(Throwable t) //@E3A In case they don't have the new service program change to match.
{
if (Trace.isTraceOn() && Trace.isTraceWarningOn()) //@E3A
{
Trace.log(Trace.WARNING, "Exception occurred while resetting static storage for DDM: ", t); //@E3A
}
}
}
//@C1A - Need this for the new createDDSSourceFile() method.
public AS400FileImplNative()
{
isNative_ = true; //@E2A
}
/**
*Closes the file on the server.
*@exception AS400Exception If the server returns an error message.
*@exception AS400SecurityException If a security or authority error occurs.
*@exception InterruptedException If this thread is interrupted.
*@exception IOException If an error occurs while communicating with the
*server.
**/
public void close()
throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{
super.close(); //@C0A
byte[] swapToPH = new byte[12];
byte[] swapFromPH = new byte[12];
boolean didSwap = system_.swapTo(swapToPH, swapFromPH);
try
{
synchronized(synch_open_close_)
{
// Close the file.
closeNtv(handle_);
}
}
catch(NativeException e)
{
// Parse the message feedback data and throw AS400Exception
throw new AS400Exception(parseMsgFeedback(e.data));
}
finally
{
if (didSwap) system_.swapBack(swapToPH, swapFromPH);
}
}
/**
*Closes the file on the server.
*@param handle the file handle.
**/
native void closeNtv(int handle)
throws NativeException;
/**
*Commits all transactions since the last commit boundary. Invoking this
*method will cause all transactions under commitment control for this
*connection to be committed. This means that any AS400File object opened
*under this connection, for which a commit lock level was specified, will
*have outstanding transactions committed. If commitment control has not been
*started for the connection, no action is taken.
*The server to which to connect must be set prior to invoking this
*method.
*@exception AS400Exception If the server returns an error message.
*@exception AS400SecurityException If a security or authority error occurs.
*@exception InterruptedException If this thread is interrupted.
*@exception IOException If an error occurs while communicating with the
*server.
**/
public void commit()
throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{
byte[] swapToPH = new byte[12];
byte[] swapFromPH = new byte[12];
boolean didSwap = system_.swapTo(swapToPH, swapFromPH);
try
{
// Commit transactions under commitment control.
commitNtv();
}
catch(NativeException e)
{
// Parse the message feedback data and throw AS400Exception
throw new AS400Exception(parseMsgFeedback(e.data));
}
finally
{
if (didSwap) system_.swapBack(swapToPH, swapFromPH);
}
}
/**
*Commits all transactions since the last commit boundary. Invoking this
*method will cause all transactions under commitment control for this
*connection to be committed. This means that any AS400File object opened
*under this connection, for which a commit lock level was specified, will
*have outstanding transactions committed. If commitment control has not been
*started for the connection, no action is taken.
*The server to which to connect must be set prior to invoking this
*method.
**/
native void commitNtv()
throws NativeException;
//@C1M 7/16/99 - Moved this code out of ImplBase to here
/**
*Creates the DDS source file to be used to create a physical file based on a user
*supplied RecordFormat.
*The name of the file and the server to which to connect must be set prior
*to invoking this method.
*@see AS400File#AS400File(com.ibm.as400.access.AS400, java.lang.String)
*@see AS400File#setPath
*@see AS400File#setSystem
*@param recordFormat The record format to describe in the DDS source file.
*@param altSeq The value to be specified for the file-level keyword ALTSEQ. If no
*value is to be specified, null may be specified.
*@param ccsid The value to be specified for the file-level keyword CCSID. If no
*value is to be specified, null may be specified.
*@param order The value to be specified to indicate in which order records are to be
*retrieved from the file. Valid values are one of the following file-level keywords:
*
*- FIFO - First in, first out
*
- LIFO - Last in, first out
*
- FCFO - First changed, first out
*
*If no ordering value is to be specified, null may be specified.
*@param ref The value to be specified for the file-level keyword REF. If no
*value is to be specified, null may be specified.
*@param unique Indicates if the file-level keyword UNIQUE is to be specified. True
*indicates that the UNIQUE keyword should be specified; false indicates that it
*should not be specified.
*@param format The value to be specified for the record-level keyword FORMAT. If no
*value is to be specified, null may be specified.
*@param text The value to be specified for the record-level keyword TEXT. If no
*value is to be specified, null may be specified.
*@exception AS400Exception If the server returns an error message.
*@exception AS400SecurityException If a security or authority error occurs.
*@exception ConnectionDroppedException If the connection is dropped unexpectedly.
*@exception InterruptedException If this thread is interrupted.
*@exception IOException If an error occurs while communicating with the server.
*@exception ServerStartupException If the host server cannot be started.
**/
public synchronized void createDDSSourceFile(RecordFormat recordFormat,
String altSeq,
String ccsid,
String order,
String ref,
boolean unique,
String format,
String text)
throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{
// Create the source physical file to hold the DDS source. Note that we create the
// file in library QTEMP. Each server job has its own QTEMP library which is created
// when the job starts and is deleted when the job ends. Using QTEMP allows
// the file to be created regardless of the user's authority and also eliminates
// name collision problems when different jobs are creating files from a record
// format.
AS400Message[] msgs = execute("QSYS/CRTSRCPF FILE(QTEMP/JT400DSSRC) MBR(*FILE) TEXT('JT400 TEMPORARY DDS SOURCE FILE')"); //@B0C
if (msgs.length > 0)
{
if (msgs[0].getID().equals("CPF5813"))
{
// File already exists from a previous create; clear it.
msgs = execute("QSYS/CLRPFM QTEMP/JT400DSSRC"); //@B0C
if (msgs.length > 0)
{
if (!msgs[0].getID().equals("CPC3101")) // "member cleared"
{
// Clear failed. Throw exception.
Trace.log(Trace.ERROR, "QSYS/CLRPFM QTEMP/JT400DSSRC");
throw new AS400Exception(msgs);
}
}
else
{
Trace.log(Trace.ERROR, "No messages were returned from QSYS/CLRPFM QTEMP/JT400DSSRC");
throw new InternalErrorException("QTEMP/JT400DSSRC", InternalErrorException.UNKNOWN);
}
}
else if (!msgs[0].getID().equals("CPC7301"))
{
// File does not exist and we were unable to create; throw exception
// (CPC7301 = Successful create)
Trace.log(Trace.ERROR, "QSYS/CRTSRCPF FILE(QTEMP/JT400DSSRC) MBR(*FILE) TEXT('JT400 TEMPORARY DDS SOURCE FILE')");
throw new AS400Exception(msgs);
}
}
else
{
// No messages. This shouldn't happen.
Trace.log(Trace.ERROR, "No messages from server.");
throw new InternalErrorException(InternalErrorException.UNKNOWN);
}
/////////////////////////////////////////////////////////////////////////////////
// Create the records to be written to the file. These records will contain the
// DDS based on the supplied RecordFormat object.
/////////////////////////////////////////////////////////////////////////////////
// Create a RecordFormat object which describes the record format of a source
// physical file.
RecordFormat srcRF = new RecordFormat("JT400DSSRC");
srcRF.addFieldDescription(new ZonedDecimalFieldDescription(new AS400ZonedDecimal(6, 2), "SRCSEQ"));
srcRF.addFieldDescription(new ZonedDecimalFieldDescription(new AS400ZonedDecimal(6, 0), "SRCDAT"));
//@C2 - This is how it should be done, but it will work the deprecated way because
// we are running on the 400.
//@C2 AS400Text text80 = new AS400Text(80, system_.getCcsid(), system_); //@C2A
//@C2 if (converter_ == null) setConverter(); //@C2A
//@C2 text80.setConverter(converter_); //@C2A
srcRF.addFieldDescription(new CharacterFieldDescription(new AS400Text(80, system_.getCcsid()), "SRCDTA"));
Vector lines = new Vector(); // Contains DDS lines to write to source file
String line; // A single line of DDS source
// Create line(s) for any file level keywords - file level keywords must precede
// the line specifying the record format name.
if (altSeq != null)
{
line = STR44 + "ALTSEQ(" + altSeq + ")";
lines.addElement(line);
}
if (ccsid != null)
{
line = STR44 + "CCSID(" + ccsid + ")";
lines.addElement(line);
}
if (order != null)
{
line = STR44 + order;
lines.addElement(line);
}
if (ref != null)
{
line = STR44 + "REF(" + ref + ")";
lines.addElement(line);
}
if (unique)
{
line = STR44 + "UNIQUE";
lines.addElement(line);
}
// Create line for the record format name
line = STR16 + "R ";
// The record format name cannot exceed 10 characters and must be in upper case
if (recordFormat.getName().length() > 10)
{
if (Trace.isTraceOn() && Trace.isTraceWarningOn())
{
Trace.log(Trace.WARNING, "Record format name '"+recordFormat.getName()+"' too long. Using '"+recordFormat.getName().substring(0,10)+"' instead.");
}
line += recordFormat.getName().substring(0, 10);
}
else
{
line += recordFormat.getName();
}
lines.addElement(line);
// Create line(s) for any record level keywords. The record level keywords
// must be on the same line or on the lines immediately following the record
// format line.
if (format != null)
{
line = STR44 + "FORMAT(" + format + ")";
lines.addElement(line);
}
if (text != null)
{
if (text.length() > 32)
{ // Text exceeds length left on line - need to continue on next line
line = STR44 + "TEXT('" + text.substring(0, 33) + "-";
lines.addElement(line);
// Add the remainder of the TEXT keyword
line = STR44 + text.substring(34) + "')";
lines.addElement(line);
}
else
{ // Text fits on one line
line = STR44 + "TEXT('" + text + "')";
lines.addElement(line);
}
}
// Create lines for each field description and any keywords for the field
int numberOfFields = recordFormat.getNumberOfFields();
FieldDescription f = null;
String ddsDesc;
String[] dds = null;
int length;
int beginningOffset;
for (int i = 0; i < numberOfFields; ++i)
{
f = recordFormat.getFieldDescription(i);
// Specify the DDS description of the field. The DDS description returned
// from FieldDescription contains the field level keywords as well as the
// description of the field. It is formatted properly for DDS except for
// the preceding blanks. Therefore, we add 18 blanks so that the field
// description starts in column 19 of the line.
dds = f.getDDSDescription();
// Add the fixed portion of the DDS description for the field to the vector
ddsDesc = STR18 + dds[0];
lines.addElement(ddsDesc);
// Add lines containing field level keywords
for (int j = 1; j < dds.length; ++j)
{
ddsDesc = STR44 + dds[j];
length = ddsDesc.length();
beginningOffset = 0;
if (length > 80)
{ // Need to continue the line on the next line
line = ddsDesc.substring(beginningOffset, 79) + "-";
lines.addElement(line);
length -= 79;
beginningOffset = 79;
line = STR44 + ddsDesc.substring(beginningOffset);
lines.addElement(line);
}
else
{ // It all fits on one line
lines.addElement(ddsDesc);
}
}
}
// Create lines for key fields and key field level keywords
numberOfFields = recordFormat.getNumberOfKeyFields();
for (int i = 0; i < numberOfFields; ++i)
{
f = recordFormat.getKeyFieldDescription(i);
// Specify the name of the field
line = STR16 + "K ";
line += f.getDDSName();
lines.addElement(line);
// Specify any key field level keywords
String[] keyFuncs = f.getKeyFieldFunctions();
if (keyFuncs != null)
{
for (short j = 0; j < keyFuncs.length; ++j)
{
line = STR44 + keyFuncs[j];
lines.addElement(line);
}
}
}
// Create an array of records representing each line to be written
// to the file.
Record[] records = new Record[lines.size()];
for (int i = 0; i < records.length; ++i)
{
records[i] = srcRF.getNewRecord();
records[i].setField("SRCSEQ", new BigDecimal(i));
records[i].setField("SRCDAT", new BigDecimal(i));
records[i].setField("SRCDTA", lines.elementAt(i));
}
// Open the DDS source file and write the records. We will write all the records
// at one time, so we specify a blocking factor of records.length on the open().
//@C1 - This is why we had to move this code out of the ImplBase, so we can instantiate.
AS400FileImplNative src = null;
try
{
src = new AS400FileImplNative();
src.setAll(system_, "/QSYS.LIB/QTEMP.LIB/JT400DSSRC.FILE", srcRF, false, false, false);
src.openFile2(AS400File.WRITE_ONLY, records.length, AS400File.COMMIT_LOCK_LEVEL_NONE, false);
src.write(records);
// Close the file
src.close();
}
catch(Exception e)
{ // log error and rethrow exception
Trace.log(Trace.ERROR, "Unable to write records to DDS source file:"+e);
try
{
if (src != null)
{
src.close();
}
}
catch(Exception e1)
{ // Ignore it; we will throw the original exception
}
if (e instanceof AS400Exception)
{
throw (AS400Exception) e;
}
else
{
Trace.log(Trace.ERROR, e);
throw new InternalErrorException(InternalErrorException.UNKNOWN, e);
}
}
}
/**
*Deletes the record at the current cursor position.
*@exception AS400Exception If the server returns an error message.
*@exception AS400SecurityException If a security or authority error occurs.
*@exception InterruptedException If this thread is interrupted.
*@exception IOException If an error occurs while communicating with the server.
**/
public void deleteCurrentRecord()
throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{
byte[] optl = {0x00, SHR_READ_NORM_RLS, DATA_DTA_DTARCD, 0x08};
byte[] swapToPH = new byte[12];
byte[] swapFromPH = new byte[12];
boolean didSwap = system_.swapTo(swapToPH, swapFromPH);
try
{
// Delete the current record.
deleteCurrentRecordNtv(handle_, optl);
}
catch(NativeException e)
{
// Parse the message feedback data and throw AS400Exception
throw new AS400Exception(parseMsgFeedback(e.data));
}
finally
{
if (didSwap) system_.swapBack(swapToPH, swapFromPH);
}
}
/**
*Deletes the record at the current cursor position. The file must be open and
*the cursor must be positioned on an active record.
*@param handle the file handle
*@exception AS400Exception If the server returns an error message.
*@exception AS400SecurityException If a security or authority error occurs.
*@exception InterruptedException If this thread is interrupted.
*@exception IOException If an error occurs while communicating with the server.
**/
native void deleteCurrentRecordNtv(int handle, byte[] optl)
throws NativeException;
/**
*Executes a command on the server.
*@param cmd the command
*@exception AS400SecurityException If a security or authority error occurs.
*@exception InterruptedException If this thread is interrupted.
*@exception IOException If an error occurs while communicating with the server.
**/
public AS400Message[] execute(String cmd)
throws AS400SecurityException, InterruptedException, IOException
{
if (converter_ == null) setConverter(); //@C2A
// Execute the command.
// Note the execution of the command is synchronized on the static
// variable synch_execute_
BytesWithOffset data = null;
byte[] swapToPH = new byte[12];
byte[] swapFromPH = new byte[12];
boolean didSwap = system_.swapTo(swapToPH, swapFromPH);
try
{
synchronized(synch_execute_)
{
data = new BytesWithOffset(executeNtv(converter_.stringToByteArray(cmd)));
}
}
finally
{
if (didSwap) system_.swapBack(swapToPH, swapFromPH);
}
// Parse the message feedback data.
AS400Message[] msgs = parseMsgFeedback(data.data_);
return msgs;
}
/**
*Executes a command on the server.
*Submits the command string via the "Process Commands" (QCAPCMD) API.
*Note: Although QCAPCMD is threadsafe, it "should not be used to run a command that is not threadsafe in a job that has multiple threads".
*@param cmd the command
*@return the message feedback data
**/
native byte[] executeNtv(byte[] cmd);
//@C0A
/**
*Prepare this object for garbage collection.
*@exception Throwable If an exception is thrown while cleaning up.
**/
public void finalization()
throws Throwable
{
// This method does nothing. It is here because finalization() is
// declared abstract in AS400FileImplBase.
}
/**
*Changes the position of the file cursor to either before the first
*record or after the last record.
*@param handle the file handle
*@param optl the options list
**/
native void forceEndOfData(int handle,
byte[] optl)
throws NativeException;
/**
*Read record(s) from the file. Does not screen out CPF5001, CPF5006.
*@param handle the file handle
*@param optl option list
*@param ctll control list
*@param length length of all record data
*@return message feedback data, I/O feedback data, record data.
**/
native byte[] getForPosition(int handle,
byte[] optl,
byte[] ctll,
int length)
throws NativeException;
/**
*Read record(s) from the file. Screens out CPF5001, CPF5006
*@param handle the file handle
*@param optl option list
*@param ctll control list
*@param length length of all record data
*@return message feedback data, I/O feedback data, record data.
**/
native byte[] getForRead(int handle,
byte[] optl,
byte[] ctll,
int length)
throws NativeException;
/**
*Read record(s) from the file by record number. Does not screen out
*CPF5001, CPF5006.
*@param handle the file handle
*@param optl option list
*@param ctll control list
*@param length length of all record data
*@return message feedback data, I/O feedback data, record data.
**/
native byte[] getdForPosition(int handle,
byte[] optl,
byte[] ctll,
int length)
throws NativeException;
/**
*Read record(s) from the file by record number. Screens out CPF5001,
*CPF5006.
*@param handle the file handle
*@param optl option list
*@param ctll control list
*@param length length of all record data
*@return message feedback data, I/O feedback data, record data.
**/
native byte[] getdForRead(int handle,
byte[] optl,
byte[] ctll,
int length)
throws NativeException;
/**
*Read record(s) from the file by key. Does not screen out CPF5001,
*CPF5006.
*@param handle the file handle
*@param optl option list
*@param ctll control list
*@param length length of all record data
*@return message feedback data, I/O feedback data, record data.
**/
native byte[] getkForPosition(int handle,
byte[] optl,
byte[] ctll,
int length)
throws NativeException;
/**
*Read record(s) from the file by key. Screens out CPF5001, CPF5006.
*@param handle the file handle
*@param optl option list
*@param ctll control list
*@param length length of all record data
*@return message feedback data, I/O feedback data, record data.
**/
native byte[] getkForRead(int handle,
byte[] optl,
byte[] ctll,
int length)
throws NativeException;
/**
*Opens the file.
*@param openType
*@param bf blocking factor
*@param access The type of file access for which to open the file.
*@return the open feedback data
*@exception AS400Exception If the server returns an error message.
*@exception AS400SecurityException If a security or authority error occurs.
*@exception ConnectionDroppedException If the connection is dropped unexpectedly.
*@exception InterruptedException If this thread is interrupted.
*@exception IOException If an error occurs while communicating with the server.
*@exception ServerStartupException If the host server cannot be started..
**/
public DDMS38OpenFeedback openFile(int openType, int bf, String access)
throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{
// Create the user file control block.
byte[] ufcb = createUFCB(openType, bf, access, true);
BytesWithOffset data = null;
byte[] swapToPH = new byte[12];
byte[] swapFromPH = new byte[12];
boolean didSwap = system_.swapTo(swapToPH, swapFromPH);
try
{
synchronized(synch_open_close_)
{
// Open the file.
data = new BytesWithOffset(openFileNtv(ufcb));
}
}
catch(NativeException e)
{
// Parse the message feedback data and throw AS400Exception
throw new AS400Exception(parseMsgFeedback(e.data));
}
finally
{
if (didSwap) system_.swapBack(swapToPH, swapFromPH);
}
// Parse the message feedback data.
AS400Message[] msgs = parseMsgFeedback(data);
// Parse the file handle.
handle_ = BinaryConverter.byteArrayToInt(data.data_, data.offset_);
data.offset_ += 4;
// Throw an exception if the open failed and there are no AS400Messages
if (handle_ <= 0)
{
Trace.log(Trace.ERROR, "Invalid handle returned from QYSTRART");
throw new InternalErrorException(InternalErrorException.UNKNOWN, handle_);
}
// Log warning messages if the open was successful (handle_ > 0)
// and there are AS400Messages
if (msgs.length > 0)
{
if (Trace.isTraceOn() && Trace.isTraceWarningOn())
{
Trace.log(Trace.WARNING, "AS400FileImplNative.openFile:");
for (int i = 0; i < msgs.length; ++i)
{
Trace.log(Trace.WARNING, msgs[i].toString());
}
}
}
// Parse the open feedback data.
openFeedback_ = new LocalOpenFeedback(system_, //@C0C
data.data_,
data.offset_);
return openFeedback_;
}
/**
*Opens the file.
*@param ufcb user file control block.
*@return message feedback data, file handle, and open feedback data
*(in that order)
**/
native byte[] openFileNtv(byte[] ufcb)
throws NativeException;
/**
*Parse the record data into records.
*@param iofb I/O feedback data
*@param data record data and offset.
*@return the records contained in the record data
**/
private Record[] parseRecordData(LocalIOFB iofb,
BytesWithOffset data)
throws UnsupportedEncodingException
{
int numberOfRecords = iofb.getNumberOfRecordsReturned();
Record[] records = new Record[numberOfRecords];
// Determine the null byte field map offset. When we opened the file, the
// the S38OPNFB reply contained the offset of the null byte field map in a record.
int nullFieldMapOffset = openFeedback_.getNullFieldByteMapOffset();
// Determine the number of fields in a record from the RecordFormat for this file.
int numFields = recordFormat_.getNumberOfFields(); //@C0C
// If the file has null capable fields, we will need to check the null byte field
// map and set the fields within the Record object as appropriate.
boolean isNullCapable = openFeedback_.isNullCapable();
for (int i = 0; i < numberOfRecords; ++i,
data.offset_ += openFeedback_.getRecordIncrement())
{
// Create the next record.
records[i] = recordFormat_.getNewRecord(data.data_, //@C0C
data.offset_);
// Set any null fields to null
if (isNullCapable)
{
for (int j = 0; j < numFields; ++j)
{ // 0xF1 = field is null, 0xF0 = field is not null
if (data.data_[nullFieldMapOffset + j + data.offset_] == (byte)0xF1)
{
records[i].setField(j, null);
}
}
}
// Set the record number. The record number is two bytes after the end
// of the record data and is four bytes long.
try
{
records[i].setRecordNumber(BinaryConverter.byteArrayToInt(data.data_, data.offset_ + iofb.getRecordLength() + 2));
}
catch(PropertyVetoException e)
{ // We created the Record objects. There is no one to veto anything
} // so this is here to quite the compiler
}
return records;
}
/**
*Parse the message feedback data into an array of AS400Message objects.
*@param data the message feedback data and offset.
*@return array of AS400Message objects (zero length array if no messages are available
**/
private AS400Message[] parseMsgFeedback(byte[] data)
throws IOException
{
AS400Message[] msgs = new AS400Message[0];
// Determine if any messages are available.
Record msgFB =
(new MessageFBFormat()).getNewRecord(data);
Short messagesOccurred = (Short) msgFB.getField("messagesOccurred");
if (messagesOccurred.intValue() == 1)
{
// Determine the number of messages.
int numMsgs = ((Short) msgFB.getField("numberOfMessages")).intValue();
if (numMsgs == 0)
{
return msgs;
}
// Loop through the array of message feedback data structures,
// building an AS400Message object for each one.
msgs = new AS400Message[numMsgs];
MessageFBDataFormat msgFBDataFormat =
new MessageFBDataFormat(system_.getCcsid()); //@C0C
Object[] feedbackData = (Object[]) msgFB.getField("feedbackData");
for (int i = 0; i < numMsgs; i++)
{
// Extract the severity, type, ID, and text from the message
// feedback.
Record msg = msgFBDataFormat.getNewRecord((byte[]) feedbackData[i]);
int severity = ((Integer) msg.getField("severityCode")).intValue();
String typeString = (String)msg.getField("messageType");
char[] typeChars = typeString.toCharArray();
int type = (typeChars[0] & 0x0F) * 10 + (typeChars[1] & 0x0F);
String id = (String) msg.getField("messageID");
int textLength = ((Integer) msg.getField("messageTextLength")).intValue();
int substitutionDataLength = ((Integer) msg.getField("replacementTextLength")).intValue();
//@B0A: We don't use the messageText field of the record format because
// it hardcodes a length of 256 bytes. If the messageText is shorter
// than 256 bytes, then we are translating junk bytes, which can
// cause problems when running under a double-byte ccsid where
// weird characters would not translate correctly.
// Instead, we use the messageTextLength field to determine how
// long the messageText is and translate it ourselves using a
// CharConverter object.
byte[] arr = (byte[])feedbackData[i]; //@B0A
String text = (new CharConverter(system_.getCcsid())).byteArrayToString(arr, 112 + substitutionDataLength, textLength); //@B0A //@C0C
//@B0D String text =
//@B0D ((String) msg.getField("messageText")).substring(0, textLength);
// Construct an AS400Message.
msgs[i] = new AS400Message(id, text);
msgs[i].setSeverity(severity);
msgs[i].setType(type);
}
}
return msgs;
}
/**
*Parse the message feedback data into an array of AS400Message objects.
*@param data the message feedback data and offset.
*@return array of AS400Message objects (zero length array if no messages are available
**/
private AS400Message[] parseMsgFeedback(BytesWithOffset data)
throws IOException
{
AS400Message[] msgs = new AS400Message[0];
// Determine if any messages are available.
Record msgFB =
(new MessageFBFormat()).getNewRecord(data.data_, data.offset_);
data.offset_ += msgFB.getRecordLength();
Short messagesOccurred = (Short) msgFB.getField("messagesOccurred");
if (messagesOccurred.intValue() == 1)
{
// Determine the number of messages.
int numMsgs = ((Short) msgFB.getField("numberOfMessages")).intValue();
if (numMsgs == 0)
{
return msgs;
}
// Loop through the array of message feedback data structures,
// building an AS400Message object for each one.
msgs = new AS400Message[numMsgs];
MessageFBDataFormat msgFBDataFormat =
new MessageFBDataFormat(system_.getCcsid()); //@C0C
Object[] feedbackData = (Object[]) msgFB.getField("feedbackData");
for (int i = 0; i < numMsgs; i++)
{
// Extract the severity, type, ID, and text from the message
// feedback.
Record msg = msgFBDataFormat.getNewRecord((byte[]) feedbackData[i]);
int severity = ((Integer) msg.getField("severityCode")).intValue();
String typeString = (String)msg.getField("messageType");
char[] typeChars = typeString.toCharArray();
int type = (typeChars[0] & 0x0F) * 10 + (typeChars[1] & 0x0F);
String id = (String) msg.getField("messageID");
int textLength = ((Integer) msg.getField("messageTextLength")).intValue();
int substitutionDataLength = ((Integer) msg.getField("replacementTextLength")).intValue();
//@B0A: We don't use the messageText field of the record format because
// it hardcodes a length of 256 bytes. If the messageText is shorter
// than 256 bytes, then we are translating junk bytes, which can
// cause problems when running under a double-byte ccsid where
// weird characters would not translate correctly.
// Instead, we use the messageTextLength field to determine how
// long the messageText is and translate it ourselves using a
// CharConverter object.
byte[] arr = (byte[])feedbackData[i]; //@B0A
String text = (new CharConverter(system_.getCcsid())).byteArrayToString(arr, 112 + substitutionDataLength, textLength); //@B0A //@C0C
//@B0D String text =
//@B0D ((String) msg.getField("messageText")).substring(0, textLength);
// Construct an AS400Message.
msgs[i] = new AS400Message(id, text);
msgs[i].setSeverity(severity);
msgs[i].setType(type);
}
}
return msgs;
}
/**
*Positions the file cursor to after the last record.
*@exception AS400Exception If the server returns an error message.
*@exception AS400SecurityException If a security or authority error occurs.
*@exception InterruptedException If this thread is interrupted.
*@exception IOException If an error occurs while communicating with the server.
**/
public void positionCursorAfterLast()
throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{
// If we are caching records and the cache contains the last record,
// position the cache. Otherwise, position the file and refresh the
// cache if we are caching records.
if (cacheRecords_ && cache_.containsLastRecord())
{
cache_.setPositionAfterLast();
return;
}
if (cacheRecords_)
{
// Invalidate the cache
cache_.setIsEmpty();
}
// Use force end of data.
byte[] optl = { TYPE_GET_LAST, SHR_READ_NORM_RLS, DATA_NODTA_DTARCD, OPER_GET };
byte[] swapToPH = new byte[12];
byte[] swapFromPH = new byte[12];
boolean didSwap = system_.swapTo(swapToPH, swapFromPH);
try
{
forceEndOfData(handle_, optl);
}
catch(NativeException e)
{
// Parse the message feedback data and throw AS400Exception
throw new AS400Exception(parseMsgFeedback(e.data));
}
finally
{
if (didSwap) system_.swapBack(swapToPH, swapFromPH);
}
}
/**
*Positions the file cursor to the specified position (first, last, next,
*previous).
*@param type the type of position operation
*@exception AS400Exception If the server returns an error message.
*@exception AS400SecurityException If a security or authority error occurs.
*@exception InterruptedException If this thread is interrupted.
*@exception IOException If an error occurs while communicating with the server.
**/
public Record[] positionCursorAt(int type)
throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{
// Use GET to position the cursor.
// byte shr = (as400File_.openType_ == AS400File.READ_ONLY ? // @A2D
// SHR_READ_NORM : SHR_UPD_NORM); // @A2D
// Start of @A2A
byte shr;
if ((openType_ == AS400File.READ_ONLY) || //@C0C
((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) //@C0C
{ // Read only
shr = SHR_READ_NORM;
}
else
{ // READ_WRITE; get for update
shr = SHR_UPD_NORM;
}
// End of @A2A
byte[] optl = { (byte) type, shr, DATA_DTA_DTARCD, OPER_GET };
byte[] ctll = new byte[6];
ctll[0] = 0x10; // ID for number of records
// Length of value field
BinaryConverter.shortToByteArray((short) 2, ctll, 1);
// Number of records to get
BinaryConverter.shortToByteArray((short) blockingFactor_, ctll, 3); //@C0C
ctll[5] = (byte)0xFF; // End of control list
BytesWithOffset data = null;
byte[] swapToPH = new byte[12];
byte[] swapFromPH = new byte[12];
boolean didSwap = system_.swapTo(swapToPH, swapFromPH);
try
{
data =
new BytesWithOffset(getForPosition(handle_, optl, ctll,
blockingFactor_ * //@C0C
openFeedback_.getRecordIncrement()));
}
catch(NativeException e)
{
// Parse the message feedback data and throw AS400Exception
throw new AS400Exception(parseMsgFeedback(e.data));
}
finally
{
if (didSwap) system_.swapBack(swapToPH, swapFromPH);
}
// Parse the I/O feedback data.
LocalIOFB iofb = new LocalIOFB(data.data_, data.offset_);
data.offset_ += 14;
// Parse the record data.
Record[] records = parseRecordData(iofb, data);
return records;
}
//@RBA
public Record[] positionCursorAtLong(int type)
throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{
return positionCursorAt(type);
}
/**
*Positions the file cursor to before the first record.
*@exception AS400Exception If the server returns an error message.
*@exception AS400SecurityException If a security or authority error occurs.
*@exception InterruptedException If this thread is interrupted.
*@exception IOException If an error occurs while communicating with the server.
**/
public void positionCursorBeforeFirst()
throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{
// If we are caching records and the cache contains the first record,
// position the cache. Otherwise, position the file and refresh the
// cache if we are caching records.
if (cacheRecords_ && cache_.containsFirstRecord())
{
cache_.setPositionBeforeFirst();
return;
}
if (cacheRecords_)
{ // Invalidate the cache
cache_.setIsEmpty();
}
// Use Force end of data to set the cursor.
byte[] optl = { TYPE_GET_FIRST, SHR_READ_NORM_RLS, DATA_NODTA_DTARCD, OPER_GET };
byte[] swapToPH = new byte[12];
byte[] swapFromPH = new byte[12];
boolean didSwap = system_.swapTo(swapToPH, swapFromPH);
try
{
forceEndOfData(handle_, optl);
}
catch(NativeException e)
{
// Parse the message feedback data and throw AS400Exception
throw new AS400Exception(parseMsgFeedback(e.data));
}
finally
{
if (didSwap) system_.swapBack(swapToPH, swapFromPH);
}
}
/**
*Positions the cursor to the record at the specified file position.
*@parm index the file position
*@exception AS400Exception If the server returns an error message.
*@exception AS400SecurityException If a security or authority error occurs.
*@exception InterruptedException If this thread is interrupted.
*@exception IOException If an error occurs while communicating with the server.
**/
public Record positionCursorToIndex(int index)
throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{
if (cacheRecords_ && !cache_.setPosition(index)) //@G0A
{
// Invalidate the cache
cache_.setIsEmpty(); //@G0A
}
// OPTL
// byte share = (as400File_.openType_ == AS400File.READ_ONLY ? // @A2D
// SHR_READ_NORM : SHR_UPD_NORM); // @A2D
// Start of @A2A
byte share;
if ((openType_ == AS400File.READ_ONLY) || //@C0C
((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) //@C0C
{ // Read only
share = SHR_READ_NORM;
}
else
{ // READ_WRITE; get for update
share = SHR_UPD_NORM;
}
// End of @A2A
byte[] optl = { TYPE_GETD_ABSRRN, share, DATA_DTA_DTARCD, OPER_GETD };
// CTLL
// ----------------------------------
// Offset Description
// ----------------------------------
// 0 record format ID
// 1 record format length
// 3 record format name
// 13 member number ID
// 14 member number length
// 16 member number value
// 18 relative record number ID
// 19 relative record number length
// 21 relative record number
// 25 control list end
byte[] ctll = new byte[26];
ctll[0] = 1;
BinaryConverter.shortToByteArray((short) 35, ctll, 1);
// Start of @A2D
/*
StringBuffer recordName =
new StringBuffer(as400File_.recordFormat_.getName());
while (recordName.length() < 10) recordName.append(' ');
converter_.stringToByteArray(recordName.toString(), ctll, 3);
*/
// End of @A2D
System.arraycopy(recordFormatCTLLName_, 0, ctll, 3, recordFormatCTLLName_.length); // @A2A //@C0C
ctll[13] = 0xf;
BinaryConverter.shortToByteArray((short) 2, ctll, 14);
BinaryConverter.shortToByteArray((short) 0, ctll, 16);
ctll[18] = 2;
BinaryConverter.shortToByteArray((short) 4, ctll, 19);
BinaryConverter.intToByteArray(index, ctll, 21);
ctll[25] = (byte) 0xff;
BytesWithOffset data = null;
byte[] swapToPH = new byte[12];
byte[] swapFromPH = new byte[12];
boolean didSwap = system_.swapTo(swapToPH, swapFromPH);
try
{
// GETD.
data =
new BytesWithOffset(getdForPosition(handle_, optl, ctll,
openFeedback_.getRecordIncrement() *
blockingFactor_)); //@C0C
}
catch(NativeException e)
{
// Parse the message feedback data and throw AS400Exception
throw new AS400Exception(parseMsgFeedback(e.data));
}
finally
{
if (didSwap) system_.swapBack(swapToPH, swapFromPH);
}
// Parse the I/O feedback data.
LocalIOFB iofb = new LocalIOFB(data.data_, data.offset_);
data.offset_ += 14;
// Parse the record data.
Record[] records = parseRecordData(iofb, data);
return (records.length == 0)? null : records[0];
}
//@RBA
public Record positionCursorToIndexLong(long index)
throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{return positionCursorToIndex((int)index);}
/**
*Positions the cursor to the first record in the file that matches the
*specified keys.
*@param keys the keys
*@param searchType the way to compare keys
*@exception AS400Exception If the server returns an error message.
*@exception AS400SecurityException If a security or authority error occurs.
*@exception InterruptedException If this thread is interrupted.
*@exception IOException If an error occurs while communicating with the server.
**/
public Record positionCursorToKey(Object[] keys,
int searchType)
throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{
// OPTL
// byte share = (as400File_.openType_ == AS400File.READ_ONLY ? // @A2D
// SHR_READ_NORM : SHR_UPD_NORM); // @A2D
if (cacheRecords_) //@G0A
{
// Invalidate the cache
cache_.setIsEmpty(); //@G0A
}
// Start of @A2A
byte share;
if ((openType_ == AS400File.READ_ONLY) || //@C0C
((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) //@C0C
{ // Read only
share = SHR_READ_NORM;
}
else
{ // READ_WRITE; get for update
share = SHR_UPD_NORM;
}
// End of @A2A
byte[] optl = { (byte)searchType, share, DATA_DTA_DTARCD, OPER_GETK };
// Determine the total length of all data in keyFields.
FieldDescription description;
ByteArrayOutputStream keyAsBytes = new ByteArrayOutputStream();
// Converter conv; // @A1D
byte[] fieldAsBytes; // Will contain each key field's data as bytes
byte[] lengthBytes = new byte[2]; // Used for variable length fields
for (int i = 0; i < keys.length; i++)
{
try
{
// Convert each key field to server data writing it to keyAsBytes
description = recordFormat_.getKeyFieldDescription(i); //@C0C
// Check if field is a variable length field. This means that the field
// is either a hex field or a character (DNCS* included) field.
// The field has the potential of being variable length, but may be
// fixed length. We account for both cases in this if.
if (description instanceof VariableLengthFieldDescription)
{
boolean varLength = ((VariableLengthFieldDescription)description).isVariableLength();
if (description instanceof HexFieldDescription)
{ // Hex field
if (varLength)
{
// Need to write two bytes of length info prior to writing the data
BinaryConverter.shortToByteArray((short)((byte[])keys[i]).length, lengthBytes, 0);
keyAsBytes.write(lengthBytes, 0, lengthBytes.length);
}
keyAsBytes.write((byte[])keys[i], 0, ((byte[])keys[i]).length);
if (varLength)
{
// We need to send the maximum field length number of bytes for the
// field, even though only keyFields[i].length bytes of the data will
// be looked at by DDM
int fieldLength = description.getDataType().getByteLength();
for (int j = ((byte[])keys[i]).length; j < fieldLength; ++j)
{
keyAsBytes.write(0);
}
}
}
else
{ // Character field
// Use Converter object to translate the key field passed in. If
// we use the data type associated with the field description, the data
// for the key field will be padded with blanks to the byteLength of
// the field description. This can cause a match of the key to occur in the
// case that the user specifies a value that does not exactly match the key
// field in the record.
if (varLength)
{
// Need to write two bytes of length info prior to writing the
// data
BinaryConverter.shortToByteArray((short)((String)keys[i]).length(), lengthBytes, 0);
keyAsBytes.write(lengthBytes, 0, lengthBytes.length);
}
// @A1D
// Modified code to use the AS400Text object to do the conversion.
// conv = Converter.getConverter(((AS400Text)description.getDataType()).getCcsid()); // @A1D
// fieldAsBytes = conv.stringToByteArray((String)keys[i]); // @A1D
fieldAsBytes = description.getDataType().toBytes(keys[i]); // @A1A
keyAsBytes.write(fieldAsBytes, 0, fieldAsBytes.length);
// We need to get rid of this now since AS400Text does the padding for us.
// @A1D
/*
if (varLength)
{
// We need to send the maximum field length number of bytes for the
// field, even though only keyFields[i].length bytes of the data will
// be looked at by DDM
int fieldLength = description.getDataType().getByteLength();
for (int j = ((String)keys[i]).length(); j < fieldLength; ++j)
{
keyAsBytes.write(0x40);
}
}
*/
}
}
else
{
// Numeric field
fieldAsBytes = description.getDataType().toBytes(keys[i]);
keyAsBytes.write(fieldAsBytes, 0, fieldAsBytes.length);
}
}
catch(NullPointerException e)
{
// One of the key fields was null
throw new ExtendedIllegalArgumentException("key", ExtendedIllegalArgumentException.PARAMETER_VALUE_NOT_VALID);
}
}
int keyLength = keyAsBytes.size();
// CTLL
// ----------------------------------
// Offset Description
// ----------------------------------
// 0 record format ID
// 1 record format length
// 3 record format name
// 13 member number ID
// 14 member number length
// 16 member number value
// 18 number of fields ID
// 19 number of fields length
// 21 number of fields value
// 25 key fields ID
// 26 key fields length,
// 28 key field values
// ?? control list end
byte[] ctll = new byte[29 + keyLength];
ctll[0] = 1;
BinaryConverter.shortToByteArray((short) 35, ctll, 1);
// Start of @A2D
/*
StringBuffer recordName =
new StringBuffer(as400File_.recordFormat_.getName());
while (recordName.length() < 10) recordName.append(' ');
converter_.stringToByteArray(recordName.toString(), ctll, 3);
*/
// End of @A2D
System.arraycopy(recordFormatCTLLName_, 0, ctll, 3, recordFormatCTLLName_.length); // @A2A //@C0C
ctll[13] = 0xf;
BinaryConverter.shortToByteArray((short) 2, ctll, 14);
BinaryConverter.shortToByteArray((short) 0, ctll, 16);
ctll[18] = 8;
BinaryConverter.shortToByteArray((short) 4, ctll, 19);
BinaryConverter.intToByteArray(keys.length, ctll, 21);
ctll[25] = 7;
BinaryConverter.shortToByteArray((short) keyLength, ctll, 26);
System.arraycopy(keyAsBytes.toByteArray(), 0, ctll, 28, keyLength);
ctll[28 + keyLength] = (byte) 0xff;
BytesWithOffset data = null;
byte[] swapToPH = new byte[12];
byte[] swapFromPH = new byte[12];
boolean didSwap = system_.swapTo(swapToPH, swapFromPH);
try
{
// GETK
data =
new BytesWithOffset(getkForPosition(handle_, optl, ctll,
openFeedback_.getRecordIncrement() *
blockingFactor_)); //@C0C
}
catch(NativeException e)
{
// Parse the message feedback data and throw AS400Exception
throw new AS400Exception(parseMsgFeedback(e.data));
}
finally
{
if (didSwap) system_.swapBack(swapToPH, swapFromPH);
}
// Parse the I/O feedback data.
// LocalIOFB iofb = new LocalIOFB(data.data_, data.offset_); // @A2D
// data.offset_ += 14; // @A2D
// Parse the record data.
// Record[] records = parseRecordData(iofb, data); // @A2D
// return (records.length == 0)? null : records[0]; // @A2D
return null;
}
//@RBA
public Record positionCursorToKeyLong(Object[] keys,
int searchType)
throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{
return positionCursorToKey(keys,searchType);
}
// @A2A
/**
*Positions the cursor to the first record in the file that matches the
*specified keys.
*@param keys the keys
*@param searchType the way to compare keys
*@exception AS400Exception If the server returns an error message.
*@exception AS400SecurityException If a security or authority error occurs.
*@exception InterruptedException If this thread is interrupted.
*@exception IOException If an error occurs while communicating with the server.
**/
public Record positionCursorToKey(byte[] keys,
int searchType, int numberOfKeyFields)
throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{
// OPTL
// byte share = (as400File_.openType_ == AS400File.READ_ONLY ?
// SHR_READ_NORM : SHR_UPD_NORM);
if (cacheRecords_) //@G0A
{
// Invalidate the cache
cache_.setIsEmpty(); //@G0A
}
// Start of @A2A
byte share;
if ((openType_ == AS400File.READ_ONLY) || //@C0C
((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) //@C0C
{ // Read only
share = SHR_READ_NORM;
}
else
{ // READ_WRITE; get for update
share = SHR_UPD_NORM;
}
// End of @A2A
byte[] optl = { (byte)searchType, share, DATA_DTA_DTARCD, OPER_GETK };
// Determine the total length of all data in keyFields.
FieldDescription description;
int keyLength = keys.length;
// CTLL
// ----------------------------------
// Offset Description
// ----------------------------------
// 0 record format ID
// 1 record format length
// 3 record format name
// 13 member number ID
// 14 member number length
// 16 member number value
// 18 number of fields ID
// 19 number of fields length
// 21 number of fields value
// 25 key fields ID
// 26 key fields length,
// 28 key field values
// ?? control list end
byte[] ctll = new byte[29 + keyLength];
ctll[0] = 1;
BinaryConverter.shortToByteArray((short) 35, ctll, 1);
// Start of @A2D
/*
StringBuffer recordName =
new StringBuffer(as400File_.recordFormat_.getName());
while (recordName.length() < 10) recordName.append(' ');
converter_.stringToByteArray(recordName.toString(), ctll, 3);
*/
// End of @A2D
System.arraycopy(recordFormatCTLLName_, 0, ctll, 3, recordFormatCTLLName_.length); // @A2A //@C0C
ctll[13] = 0xf;
BinaryConverter.shortToByteArray((short) 2, ctll, 14);
BinaryConverter.shortToByteArray((short) 0, ctll, 16);
ctll[18] = 8;
BinaryConverter.shortToByteArray((short) 4, ctll, 19);
BinaryConverter.intToByteArray(numberOfKeyFields, ctll, 21);
ctll[25] = 7;
BinaryConverter.shortToByteArray((short) keyLength, ctll, 26);
System.arraycopy(keys, 0, ctll, 28, keyLength);
ctll[28 + keyLength] = (byte) 0xff;
BytesWithOffset data = null;
byte[] swapToPH = new byte[12];
byte[] swapFromPH = new byte[12];
boolean didSwap = system_.swapTo(swapToPH, swapFromPH);
try
{
// GETK
data =
new BytesWithOffset(getkForPosition(handle_, optl, ctll,
openFeedback_.getRecordIncrement() *
blockingFactor_)); //@C0C
}
catch(NativeException e)
{
// Parse the message feedback data and throw AS400Exception
throw new AS400Exception(parseMsgFeedback(e.data));
}
finally
{
if (didSwap) system_.swapBack(swapToPH, swapFromPH);
}
// Parse the I/O feedback data.
// LocalIOFB iofb = new LocalIOFB(data.data_, data.offset_); // @A2D
// data.offset_ += 14; // @A2D
// Parse the record data.
// Record[] records = parseRecordData(iofb, data); // @A2D
// return (records.length == 0)? null : records[0]; // @A2D
return null;
}
//@RBA
public Record positionCursorToKeyLong(byte[] keys,
int searchType, int numberOfKeyFields)
throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{return positionCursorToKey(keys,searchType,numberOfKeyFields);}
/**
*Write records to the file.
*@param handle the file handle.
*@param optl option list
*@param ctll control list
*@param data record data
**/
native void put(int handle,
byte[] optl,
byte[] ctll,
byte[] data)
throws NativeException;
/**
Reads the record at the specified file position.
@param index the file position
@return the record read.
*@exception AS400Exception If the server returns an error message.
*@exception AS400SecurityException If a security or authority error occurs.
*@exception InterruptedException If this thread is interrupted.
*@exception IOException If an error occurs while communicating with the server.
**/
public Record read(int index)
throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{
if (cacheRecords_) //@C0A
return super.read(index); //@C0A
// OPTL
// byte share = (as400File_.openType_ == AS400File.READ_ONLY ? // @A2D
// SHR_READ_NORM : SHR_UPD_NORM); // @A2D
// Start of @A2A
byte share;
if ((openType_ == AS400File.READ_ONLY) || //@C0C
((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) //@C0C
{ // Read only
share = SHR_READ_NORM;
}
else
{ // READ_WRITE; get for update
share = SHR_UPD_NORM;
}
// End of @A2A
byte[] optl = { TYPE_GETD_ABSRRN, share, DATA_DTA_DTARCD, OPER_GETD };
// CTLL
// ----------------------------------
// Offset Description
// ----------------------------------
// 0 record format ID
// 1 record format length
// 3 record format name
// 13 member number ID
// 14 member number length
// 16 member number value
// 18 relative record number ID
// 19 relative record number length
// 21 relative record number
// 25 control list end
byte[] ctll = new byte[26];
ctll[0] = 1;
BinaryConverter.shortToByteArray((short) 35, ctll, 1);
// Start of @A2D
/*
StringBuffer recordName =
new StringBuffer(as400File_.recordFormat_.getName());
while (recordName.length() < 10) recordName.append(' ');
converter_.stringToByteArray(recordName.toString(), ctll, 3);
*/
// End of @A2D
System.arraycopy(recordFormatCTLLName_, 0, ctll, 3, recordFormatCTLLName_.length); // @A2A //@C0C
ctll[13] = 0xf;
BinaryConverter.shortToByteArray((short) 2, ctll, 14);
BinaryConverter.shortToByteArray((short) 0, ctll, 16);
ctll[18] = 2;
BinaryConverter.shortToByteArray((short) 4, ctll, 19);
BinaryConverter.intToByteArray(index, ctll, 21);
ctll[25] = (byte) 0xff;
BytesWithOffset data = null;
byte[] swapToPH = new byte[12];
byte[] swapFromPH = new byte[12];
boolean didSwap = system_.swapTo(swapToPH, swapFromPH);
try
{
// GETD
data =
new BytesWithOffset(getdForRead(handle_, optl, ctll,
openFeedback_.getRecordIncrement() *
blockingFactor_)); //@C0C
}
catch(NativeException e)
{
// Parse the message feedback data and throw AS400Exception
throw new AS400Exception(parseMsgFeedback(e.data));
}
finally
{
if (didSwap) system_.swapBack(swapToPH, swapFromPH);
}
// Parse the I/O feedback data.
LocalIOFB iofb = new LocalIOFB(data.data_, data.offset_);
data.offset_ += 14;
// Parse the record data.
Record[] records = parseRecordData(iofb, data);
return (records.length == 0)? null : records[0];
}
/**
*Reads the first record with the specified key based on the specified search type.
*@param key The values that make up the key with which to find the record.
*@param searchType The type of read. This value is one of the TYPE_GETKEY_* constants.
*@return The record read.
*@exception AS400Exception If the server returns an error message.
*@exception AS400SecurityException If a security or authority error occurs.
*@exception InterruptedException If this thread is interrupted.
*@exception IOException If an error occurs while communicating with the server.
**/
public Record read(Object[] key,
int searchType)
throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{
// OPTL
// byte share = (as400File_.openType_ == AS400File.READ_ONLY ? // @A2D
// SHR_READ_NORM : SHR_UPD_NORM); // @A2D
// Start of @A2A
byte share;
if ((openType_ == AS400File.READ_ONLY) || //@C0C
((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) //@C0C
{ // Read only
share = SHR_READ_NORM;
}
else
{ // READ_WRITE; get for update
share = SHR_UPD_NORM;
}
// End of @A2A
byte[] optl = { (byte)searchType, share, DATA_DTA_DTARCD, OPER_GETK };
// Determine the total length of all data in keyFields.
FieldDescription description;
ByteArrayOutputStream keyAsBytes = new ByteArrayOutputStream();
// Converter conv; // @A1D
byte[] fieldAsBytes; // Will contain each key field's data as bytes
byte[] lengthBytes = new byte[2]; // Used for variable length fields
for (int i = 0; i < key.length; i++)
{
try
{
// Convert each key field to server data writing it to keyAsBytes
description = recordFormat_.getKeyFieldDescription(i); //@C0C
// Check if field is a variable length field. This means that the field
// is either a hex field or a character (DNCS* included) field.
// The field has the potential of being variable length, but may be
// fixed length. We account for both cases in this if.
if (description instanceof VariableLengthFieldDescription)
{
boolean varLength = ((VariableLengthFieldDescription)description).isVariableLength();
if (description instanceof HexFieldDescription)
{ // Hex field
if (varLength)
{
// Need to write two bytes of length info prior to writing the data
BinaryConverter.shortToByteArray((short)((byte[])key[i]).length, lengthBytes, 0);
keyAsBytes.write(lengthBytes, 0, lengthBytes.length);
}
keyAsBytes.write((byte[])key[i], 0, ((byte[])key[i]).length);
if (varLength)
{
// We need to send the maximum field length number of bytes for the
// field, even though only keyFields[i].length bytes of the data will
// be looked at by DDM
int fieldLength = description.getDataType().getByteLength();
for (int j = ((byte[])key[i]).length; j < fieldLength; ++j)
{
keyAsBytes.write(0);
}
}
}
else
{ // Character field
// Use Converter object to translate the key field passed in. If
// we use the data type associated with the field description, the data
// for the key field will be padded with blanks to the byteLength of
// the field description. This can cause a match of the key to occur in the
// case that the user specifies a value that does not exactly match the key
// field in the record.
if (varLength)
{
// Need to write two bytes of length info prior to writing the
// data
BinaryConverter.shortToByteArray((short)((String)key[i]).length(), lengthBytes, 0);
keyAsBytes.write(lengthBytes, 0, lengthBytes.length);
}
// conv = Converter.getConverter(((AS400Text)description.getDataType()).getCcsid()); // @A1D
// fieldAsBytes = conv.stringToByteArray((String)key[i]); // @A1D
fieldAsBytes = description.getDataType().toBytes(key[i]); // @A1A
keyAsBytes.write(fieldAsBytes, 0, fieldAsBytes.length);
// @A1D
/*
if (varLength)
{
// We need to send the maximum field length number of bytes for the
// field, even though only keyFields[i].length bytes of the data will
// be looked at by DDM
int fieldLength = description.getDataType().getByteLength();
for (int j = ((String)key[i]).length(); j < fieldLength; ++j)
{
keyAsBytes.write(0x40);
}
}
*/
}
}
else
{
// Numeric field
fieldAsBytes = description.getDataType().toBytes(key[i]);
keyAsBytes.write(fieldAsBytes, 0, fieldAsBytes.length);
}
}
catch(NullPointerException e)
{
// One of the key fields was null
throw new ExtendedIllegalArgumentException("key", ExtendedIllegalArgumentException.PARAMETER_VALUE_NOT_VALID);
}
}
int keyLength = keyAsBytes.size();
// CTLL
// ----------------------------------
// Offset Description
// ----------------------------------
// 0 record format ID
// 1 record format length
// 3 record format name
// 13 member number ID
// 14 member number length
// 16 member number value
// 18 number of fields ID
// 19 number of fields length
// 21 number of fields value
// 25 key fields ID
// 26 key fields length,
// 28 key field values
// ?? control list end
byte[] ctll = new byte[29 + keyLength];
ctll[0] = 1;
BinaryConverter.shortToByteArray((short) 35, ctll, 1);
// Start of @A2D
/*
StringBuffer recordName =
new StringBuffer(as400File_.recordFormat_.getName());
while (recordName.length() < 10) recordName.append(' ');
converter_.stringToByteArray(recordName.toString(), ctll, 3);
*/
// End of @A2D
System.arraycopy(recordFormatCTLLName_, 0, ctll, 3, recordFormatCTLLName_.length); // @A2A //@C0C
ctll[13] = 0xf;
BinaryConverter.shortToByteArray((short) 2, ctll, 14);
BinaryConverter.shortToByteArray((short) 0, ctll, 16);
ctll[18] = 8;
BinaryConverter.shortToByteArray((short) 4, ctll, 19);
BinaryConverter.intToByteArray(key.length, ctll, 21);
ctll[25] = 7;
BinaryConverter.shortToByteArray((short) keyLength, ctll, 26);
System.arraycopy(keyAsBytes.toByteArray(), 0, ctll, 28, keyLength);
ctll[28 + keyLength] = (byte) 0xff;
BytesWithOffset data = null;
byte[] swapToPH = new byte[12];
byte[] swapFromPH = new byte[12];
boolean didSwap = system_.swapTo(swapToPH, swapFromPH);
try
{
// GETK.
data =
new BytesWithOffset(getkForRead(handle_, optl, ctll,
openFeedback_.getRecordIncrement() *
blockingFactor_)); //@C0C
}
catch(NativeException e)
{
// Parse the message feedback data and throw AS400Exception
throw new AS400Exception(parseMsgFeedback(e.data));
}
finally
{
if (didSwap) system_.swapBack(swapToPH, swapFromPH);
}
// Parse the I/O feedback data.
LocalIOFB iofb = new LocalIOFB(data.data_, data.offset_);
data.offset_ += 14;
// Parse the record data.
Record[] records = parseRecordData(iofb, data);
if (cacheRecords_) //@C0A
{ //@C0A
cache_.setIsEmpty(); //@C0A
} //@C0A
return (records.length == 0)? null : records[0];
}
//@RBA
public Record readLong(Object[] key,
int searchType)
throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{
return null;
}
// @A2A
/**
*Reads the first record with the specified key based on the specified search type.
*@param key The values that make up the key with which to find the record.
*@param searchType The type of read. This value is one of the TYPE_GETKEY_* constants.
*@return The record read.
*@exception AS400Exception If the server returns an error message.
*@exception AS400SecurityException If a security or authority error occurs.
*@exception InterruptedException If this thread is interrupted.
*@exception IOException If an error occurs while communicating with the server.
**/
public Record read(byte[] key,
int searchType, int numberOfKeyFields)
throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{
// OPTL
// byte share = (as400File_.openType_ == AS400File.READ_ONLY ?
// SHR_READ_NORM : SHR_UPD_NORM);
// Start of @A2A
byte share;
if ((openType_ == AS400File.READ_ONLY) || //@C0C
((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) //@C0C
{ // Read only
share = SHR_READ_NORM;
}
else
{ // READ_WRITE; get for update
share = SHR_UPD_NORM;
}
// End of @A2A
byte[] optl = { (byte)searchType, share, DATA_DTA_DTARCD, OPER_GETK };
// Determine the total length of all data in keyFields.
FieldDescription description;
int keyLength = key.length;
// CTLL
// ----------------------------------
// Offset Description
// ----------------------------------
// 0 record format ID
// 1 record format length
// 3 record format name
// 13 member number ID
// 14 member number length
// 16 member number value
// 18 number of fields ID
// 19 number of fields length
// 21 number of fields value
// 25 key fields ID
// 26 key fields length,
// 28 key field values
// ?? control list end
byte[] ctll = new byte[29 + keyLength];
ctll[0] = 1;
BinaryConverter.shortToByteArray((short) 35, ctll, 1);
// Start of @A2D
/*
StringBuffer recordName =
new StringBuffer(as400File_.recordFormat_.getName());
while (recordName.length() < 10) recordName.append(' ');
converter_.stringToByteArray(recordName.toString(), ctll, 3);
*/
// Start of @A2D
System.arraycopy(recordFormatCTLLName_, 0, ctll, 3, recordFormatCTLLName_.length); // @A2A //@C0C
ctll[13] = 0xf;
BinaryConverter.shortToByteArray((short) 2, ctll, 14);
BinaryConverter.shortToByteArray((short) 0, ctll, 16);
ctll[18] = 8;
BinaryConverter.shortToByteArray((short) 4, ctll, 19);
BinaryConverter.intToByteArray(numberOfKeyFields, ctll, 21);
ctll[25] = 7;
BinaryConverter.shortToByteArray((short) keyLength, ctll, 26);
System.arraycopy(key, 0, ctll, 28, keyLength);
ctll[28 + keyLength] = (byte) 0xff;
BytesWithOffset data = null;
byte[] swapToPH = new byte[12];
byte[] swapFromPH = new byte[12];
boolean didSwap = system_.swapTo(swapToPH, swapFromPH);
try
{
// GETK.
data =
new BytesWithOffset(getkForRead(handle_, optl, ctll,
openFeedback_.getRecordIncrement() *
blockingFactor_)); //@C0C
}
catch(NativeException e)
{
// Parse the message feedback data and throw AS400Exception
throw new AS400Exception(parseMsgFeedback(e.data));
}
finally
{
if (didSwap) system_.swapBack(swapToPH, swapFromPH);
}
// Parse the I/O feedback data.
LocalIOFB iofb = new LocalIOFB(data.data_, data.offset_);
data.offset_ += 14;
// Parse the record data.
Record[] records = parseRecordData(iofb, data);
if (cacheRecords_) //@C0A
{ //@C0A
cache_.setIsEmpty(); //@C0A
} //@C0A
return (records.length == 0)? null : records[0];
}
public Record readLong(byte[] key,
int searchType, int numberOfKeyFields)
throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{
return read(key,searchType,numberOfKeyFields);
}
/**
*Reads all the records in the file.
*@param fileType The type of file. Valid values are: key or seq
*@return The records read.
*@exception AS400Exception If the server returns an error message.
*@exception AS400SecurityException If a security or authority error occurs.
*@exception InterruptedException If this thread is interrupted.
*@exception IOException If an error occurs while communicating with the server.
**/
public Record[] readAll(String fileType, int bf) //@C1C
throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{
// Open the file with a blocking factor of 100.
openFile2(AS400File.READ_ONLY, bf, AS400File.COMMIT_LOCK_LEVEL_NONE, fileType); //@C0C @C1C
Record[] records = null;
try
{
// Determine how many records are in the file.
records = new Record[openFeedback_.getNumberOfRecords()];
// Read the entire file contents one record at a time.
for (int i = 0; i < records.length; i++)
{
records[i] = readNext(); //@C0C
}
}
finally
{
// Close the file we opened.
try
{
close(); //@C0C
}
catch(AS400Exception e)
{
resetState(); //@C0C
throw e;
}
catch(AS400SecurityException e)
{
resetState(); //@C0C
throw e;
}
catch(InterruptedException e)
{
resetState(); //@C0C
throw e;
}
catch(IOException e)
{
resetState(); //@C0C
throw e;
}
}
return records;
}
//@RBA
public Record[] readAllLong(String fileType, int bf) //@C1C
throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{
return readAll( fileType, bf);
}
//@RBA
public Record readRecordLong(int type)
throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{return readRecord(type);}
/**
*Reads the record at the current file position.
*@param type type of read (first, last, next, previous)
*@return the record read.
*@exception AS400Exception If the server returns an error message.
*@exception AS400SecurityException If a security or authority error occurs.
*@exception InterruptedException If this thread is interrupted.
*@exception IOException If an error occurs while communicating with the server.
**/
public Record readRecord(int type)
throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{
// OPTL
// byte share = (as400File_.openType_ == AS400File.READ_ONLY ? // @A2D
// SHR_READ_NORM : SHR_UPD_NORM); // @A2D
// Start of @A2A
byte share;
if ((openType_ == AS400File.READ_ONLY) || //@C0C
((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) //@C0C
{ // Read only
share = SHR_READ_NORM;
}
else
{ // READ_WRITE; get for update
share = SHR_UPD_NORM;
}
// End of @A2A
byte[] optl = { (byte) type, share, DATA_DTA_DTARCD, OPER_GET };
byte[] ctll = new byte[6];
ctll[0] = 0x10; // ID for number of records
// Length of value field
BinaryConverter.shortToByteArray((short) 2, ctll, 1);
// Number of records to get
BinaryConverter.shortToByteArray((short) blockingFactor_, ctll, 3); //@C0C
ctll[5] = (byte)0xFF; // End of control list
BytesWithOffset data = null;
byte[] swapToPH = new byte[12];
byte[] swapFromPH = new byte[12];
boolean didSwap = system_.swapTo(swapToPH, swapFromPH);
try
{
// GET
data =
new BytesWithOffset(getForRead(handle_, optl, ctll,
openFeedback_.getRecordIncrement() *
blockingFactor_)); //@C0C
}
catch(NativeException e)
{
// Parse the message feedback data and throw AS400Exception
throw new AS400Exception(parseMsgFeedback(e.data));
}
finally
{
if (didSwap) system_.swapBack(swapToPH, swapFromPH);
}
// Parse the I/O feedback data.
LocalIOFB iofb = new LocalIOFB(data.data_, data.offset_);
data.offset_ += 14;
// Parse the record data.
Record[] records = parseRecordData(iofb, data);
return (records.length == 0)? null : records[0];
}
//@RBA
public Record[] readRecordsLong(int direction)
throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{
return null;
}
/**
*Reads records from the file. The next or previous 'blockingFactor_'
*records are retrieved depending on the direction specified.
*@param direction (DDMRecordCache.FORWARD or DDMRecordCache.BACKWARD)
*@return the records read
*@exception AS400Exception If the server returns an error message.
*@exception AS400SecurityException If a security or authority error occurs.
*@exception InterruptedException If this thread is interrupted.
*@exception IOException If an error occurs while communicating with the server.
**/
public Record[] readRecords(int direction)
throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{
// OPTL
byte type = (direction == DDMRecordCache.FORWARD ? TYPE_GET_NEXT :
TYPE_GET_PREV);
byte[] optl = { type, SHR_READ_NORM, DATA_DTA_DTARCD, OPER_GET };
byte[] ctll = new byte[6];
ctll[0] = 0x10; // ID for number of records
// Length of value field
BinaryConverter.shortToByteArray((short) 2, ctll, 1);
// Number of records to get
BinaryConverter.shortToByteArray((short) blockingFactor_, ctll, 3); //@C0C
ctll[5] = (byte)0xFF; // End of control list
// GET.
int length = openFeedback_.getRecordIncrement() *
blockingFactor_; //@C0C
BytesWithOffset data = null;
byte[] swapToPH = new byte[12];
byte[] swapFromPH = new byte[12];
boolean didSwap = system_.swapTo(swapToPH, swapFromPH);
try
{
data =
new BytesWithOffset(getForRead(handle_, optl, ctll, length));
}
catch(NativeException e)
{
// Parse the message feedback data and throw AS400Exception
throw new AS400Exception(parseMsgFeedback(e.data));
}
finally
{
if (didSwap) system_.swapBack(swapToPH, swapFromPH);
}
// Parse the I/O feedback data.
LocalIOFB iofb = new LocalIOFB(data.data_, data.offset_);
data.offset_ += 14;
// Parse the record data.
Record[] records = parseRecordData(iofb, data);
return records;
}
//@E3A
/**
* Used to reset the static hashtable of file handles that is
* stored in the native code. We do this now because the
* service program does not get reactivated when
* another JVM is instantiated (as of the release after v4r5).
**/
private static native void resetStaticStorage();
/**
*Rolls back any transactions since the last commit/rollback boundary. Invoking this
*method will cause all transactions under commitment control for this connection
*to be rolled back. This means that any AS400File object for which a commit
*lock level was specified and that was opened under this connection will have
*outstanding transactions rolled back.
*@exception AS400Exception If the server returns an error message.
*@exception AS400SecurityException If a security or authority error occurs.
*@exception InterruptedException If this thread is interrupted.
*@exception IOException If an error occurs while communicating with the server.
**/
public void rollback()
throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{
byte[] swapToPH = new byte[12];
byte[] swapFromPH = new byte[12];
boolean didSwap = system_.swapTo(swapToPH, swapFromPH);
try
{
// Rollback.
rollbackNtv();
}
catch(NativeException e)
{
// Parse the message feedback data and throw AS400Exception
throw new AS400Exception(parseMsgFeedback(e.data));
}
finally
{
if (didSwap) system_.swapBack(swapToPH, swapFromPH);
}
}
/**
*Rollback changes to files under commitment control.
**/
native void rollbackNtv()
throws NativeException;
/**
*Updates the specified record.
*@param handle the file handle
*@param optl option list
*@param ctll control list
*@param recordData record data
**/
native void updat(int handle,
byte[] optl,
byte[] ctll,
byte[] recordData)
throws NativeException;
/**
*Updates the record at the current cursor position.
*@exception AS400Exception If the server returns an error message.
*@exception AS400SecurityException If a security or authority error occurs.
*@exception InterruptedException If this thread is interrupted.
*@exception IOException If an error occurs while communicating with the server.
**/
public void update(Record record)
throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{
if (record.getRecordLength() != openFeedback_.getRecordLength())
{
if (Trace.isTraceOn() && Trace.isTraceErrorOn())
{
Trace.log(Trace.ERROR, "Incorrect record length for file :");
Trace.log(Trace.ERROR, "record.getRecordLength() :" + String.valueOf(record.getRecordLength()));
}
throw new ExtendedIllegalArgumentException("record", ExtendedIllegalArgumentException. PARAMETER_VALUE_NOT_VALID);
}
// OPTL
byte[] optl = { TYPE_GET_SAME, SHR_UPD_NORM, DATA_DTA_DTARCD, OPER_UPDATE };
// Create the record data to be sent
int recordIncrement = openFeedback_.getRecordIncrement();
byte[] recordData = new byte[recordIncrement];
System.arraycopy(record.getContents(), 0, recordData, 0, record.getRecordLength());
// Start of changes for ----------------------------------------- @A3A
// Determine the maxNumberOfFieldsPerFormatInFile for the file
// remembering that multi-format logical files may have a different
// number of getNumberOfFields() per record format.
// DDMObjectDataStream.getObjectS38BUF() needs maxNumberOfFieldsPerFormatInFile
int numFields = record.getNumberOfFields();
int maxNumberOfFieldsPerFormatInFile = numFields;
if (rfCache_ == null)
{
// Use default maxNumberOfFieldsPerFormatInFile == numFields
}
else // Determine if a different record format has more fields (i.e. find the max)
{
int numberOfRecordFormats = rfCache_.length;
for(int i = 0; i < numberOfRecordFormats; ++i)
{
maxNumberOfFieldsPerFormatInFile =
Math.max(maxNumberOfFieldsPerFormatInFile, rfCache_[i].getNumberOfFields());
}
}
// End of changes for ------------------------------------------- @A3A
int fieldOffset = recordIncrement - maxNumberOfFieldsPerFormatInFile; //@A3C
for (int i = 0; i < numFields; ++i, ++fieldOffset)
{
recordData[fieldOffset] = (record.isNullField(i))? (byte)0xF1 : (byte)0xF0;
}
byte[] swapToPH = new byte[12];
byte[] swapFromPH = new byte[12];
boolean didSwap = system_.swapTo(swapToPH, swapFromPH);
try
{
// UPDAT
updat(handle_, optl, null, recordData);
}
catch(NativeException e)
{
// Parse the message feedback data and throw AS400Exception
throw new AS400Exception(parseMsgFeedback(e.data));
}
finally
{
if (didSwap) system_.swapBack(swapToPH, swapFromPH);
}
}
/**
*Writes an array of records to the end of the file.
*The cursor is positioned to after the last record of the file as a result
*of invoking this method.
*@param records The records to write. The records must have a format
*which matches the record format of this object. To ensure that this
*requirement is met, use the
*RecordFormat.getNewRecord()
*method to obtain default records whose fields can be set appropriately by
*the Java program and then written to the file.
*@exception AS400Exception If the server returns an error message.
*@exception AS400SecurityException If a security or authority error occurs.
*@exception InterruptedException If this thread is interrupted.
*@exception IOException If an error occurs while communicating with the server.
**/
public void write(Record[] records)
throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{
if (records[0].getRecordLength() != openFeedback_.getRecordLength())
{
if (Trace.isTraceOn() && Trace.isTraceErrorOn())
{
Trace.log(Trace.ERROR, "Incorrect record length for file :");
Trace.log(Trace.ERROR, "record.getRecordLength() :" + String.valueOf(records[0].getRecordLength()));
}
throw new ExtendedIllegalArgumentException("records", ExtendedIllegalArgumentException. PARAMETER_VALUE_NOT_VALID);
}
// PUT the record data to the file, blocking factor records at a time.
int recordIncrement = openFeedback_.getRecordIncrement();
byte[] recordData =
new byte[(records.length < blockingFactor_ ? //@C0C
records.length : blockingFactor_) * //@C0C
recordIncrement];
// Start of changes for ----------------------------------------- @A3A
// Determine the maxNumberOfFieldsPerFormatInFile for the file
// remembering that multi-format logical files may have a different
// number of getNumberOfFields() per record format.
// DDMObjectDataStream.getObjectS38BUF() needs maxNumberOfFieldsPerFormatInFile
int maxNumberOfFieldsPerFormatInFile = -1;
if (rfCache_ == null)
{
// Use default maxNumberOfFieldsPerFormatInFile == -1
}
else // Determine if a different record format has more fields (i.e. find the max)
{
int numberOfRecordFormats = rfCache_.length;
for(int i = 0; i < numberOfRecordFormats; ++i)
{
maxNumberOfFieldsPerFormatInFile =
Math.max(maxNumberOfFieldsPerFormatInFile, rfCache_[i].getNumberOfFields());
}
}
// End of changes for ------------------------------------------- @A3A
for (int offset = 0, r = 1; r <= records.length; r++)
{
// Copy the record data to the record data buffer.
byte[] record = records[r-1].getContents();
System.arraycopy(record, 0, recordData, offset, record.length);
// Write the null field byte map array after the record data. It
// immediately preceeds the next record. 0xf1 = null, 0xf0 != null
// There may be a gap between the end of the record data and the
// start of the null field byte map.
int numFields = records[r-1].getNumberOfFields();
if (maxNumberOfFieldsPerFormatInFile == -1) //@A3A
{ //@A3A
maxNumberOfFieldsPerFormatInFile = numFields; //@A3A
} //@A3A
for (int f = 0, fieldOffset = offset + //@A3C
(recordIncrement - maxNumberOfFieldsPerFormatInFile); f < numFields; fieldOffset++, f++)
{
recordData[fieldOffset] =
(records[r-1].isNullField(f) ? (byte) 0xf1 : (byte) 0xf0);
}
// If we've accumulated the data from blocking factor records or
// have added data from the last record, PUT the record data.
if (r == records.length && r > blockingFactor_ && r % blockingFactor_ != 0) //@C0C
{
byte[] dataToWrite = new byte[offset + recordIncrement];
System.arraycopy(recordData, 0, dataToWrite, 0, dataToWrite.length);
byte[] swapToPH = new byte[12];
byte[] swapFromPH = new byte[12];
boolean didSwap = system_.swapTo(swapToPH, swapFromPH);
try
{
// PUT
put(handle_, null, null, dataToWrite);
}
catch(NativeException e)
{
// Parse the message feedback data and throw AS400Exception
throw new AS400Exception(parseMsgFeedback(e.data));
}
finally
{
if (didSwap) system_.swapBack(swapToPH, swapFromPH);
}
}
else if (r == records.length || (r % blockingFactor_ == 0)) //@C0C
{
byte[] swapToPH = new byte[12];
byte[] swapFromPH = new byte[12];
boolean didSwap = system_.swapTo(swapToPH, swapFromPH);
try
{
// PUT
put(handle_, null, null, recordData);
}
catch(NativeException e)
{
// Parse the message feedback data and throw AS400Exception
throw new AS400Exception(parseMsgFeedback(e.data));
}
finally
{
if (didSwap) system_.swapBack(swapToPH, swapFromPH);
}
// Reset the offset.
offset = 0;
}
else
{
// Increment the offset.
offset += recordIncrement;
}
}
}
}