com.ibm.as400.access.AS400FileImplRemote Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jt400 Show documentation
Show all versions of jt400 Show documentation
The Open Source version of the IBM Toolbox for Java
The newest version!
///////////////////////////////////////////////////////////////////////////////
//
// JTOpen (IBM Toolbox for Java - OSS version)
//
// Filename: AS400FileImplRemote.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.
//
///////////////////////////////////////////////////////////////////////////////
//
// @A2 - 09/19/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 the corresponding changes in
// DDMObjectDataStream.java (@A1)
//
///////////////////////////////////////////////////////////////////////////////
package com.ibm.as400.access;
import java.beans.PropertyVetoException;
import java.io.IOException;
import java.io.Serializable;
import java.net.UnknownHostException;
import java.util.Vector;
import java.math.BigDecimal; //@D0A 7/15/99
import java.lang.Math; //@A2A
class AS400FileImplRemote extends AS400FileImplBase implements Serializable //@C0C
{
static final long serialVersionUID = 4L;
//-------------------------------------------------------
// VARIABLES
//-------------------------------------------------------
// Flag indicating to ignore replys (used by close and execute).
//@D1D (moved to AS400FileImplBase) private boolean discardReplys_ = false;
// The declared file name. This is an alias for the file which allows
// DDM to process some file requests more quickly. The declared file name is
// determined upon construction and set when the file is opened.
byte[] dclName_ = new byte[8];
// Static variable used to ensure that each AS400File object has a unique
// declared file name.
static long nextDCLName_ = 1;
static final Object nextDCLNameLock_ = new Object();
// S38BUF data for force end of data calls. Contain S38BUF LL, CP and value.
// @F1 - also use this for locking
static private byte[] s38Buffer = {0x00, 0x05, (byte)0xD4, 0x05, 0x00};
// Server
transient AS400Server server_ = null;
// @B1A
private static int lastCorrelationId_ = 0; //@B6C
private static Object correlationIdLock_ = new Object(); //@B6A
// Identify the DDM reply data streams to the AS400Server class.
static
{
AS400Server.addReplyStream(new DDMObjectDataStream(), AS400.RECORDACCESS); //@B5C
AS400Server.addReplyStream(new DDMReplyDataStream(), AS400.RECORDACCESS); //@B5C
}
// Instantiate a default AS400File remote implementation object.
public AS400FileImplRemote()
{
setDCLName();
}
/**
*Closes the file on the server. All file locks held by this connection
*are released. All
*uncommitted transactions against the file are rolled back if commitment
*control has been started.
*@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
// Send the close file data stream request
if (discardReplys_ && (server_ != null))
{
synchronized(server_) //@F1A
{ //@F1A
server_.sendAndDiscardReply(DDMRequestDataStream.getRequestS38CLOSE(dclName_),newCorrelationId());//@M8C
} //@F1A
}
else
{
Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38CLOSE(dclName_), /*server_.*/newCorrelationId()); // @B1M
// Reply expected: S38MSGRM, severity code 0
if (!(replys.size() == 1 && verifyS38MSGRM((DDMDataStream)replys.elementAt(0), null, 0)))
{
handleErrorReply(replys, 0);
}
}
}
/**
*Commits all transactions since the last commit boundary. Invoking this
*method will cause all transactions under commitment control for this
*connection to be committed.
*@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.
*@exception UnknownHostException If the server cannot be located.
**/
public void commit()
throws AS400Exception,
AS400SecurityException,
InterruptedException,
IOException
{
// Connect to the server. Note: If we have already connected, that connection
// will be used.
connect();
// Send the commit data stream request
Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestCMMUOW(), newCorrelationId()); //@B6C
// Reply expected: ENDUOWRM, with UOWDSP parameter = 1
if (replys.size() == 1 && ((DDMDataStream)replys.elementAt(0)).getCodePoint() == DDMTerm.ENDUOWRM)
{
// Check the UOWDSP term value
DDMEndUnitOfWorkReply uowReply = new DDMEndUnitOfWorkReply(((DDMDataStream)replys.elementAt(0)).data_);
if (uowReply.getStatus() != 0x01)
{
// Status of logical unit of work committed not returned; should not happen unless
// we are constructing the request wrong.
Trace.log(Trace.ERROR, "Wrong status returned from commit ds", uowReply.data_);
throw new InternalErrorException(InternalErrorException.UNKNOWN, uowReply.getStatus());
}
}
else
{
handleErrorReply(replys, 0);
}
}
/**
*Connects to the server. The name and system must have been
*set at this point via the constructor or the setters.
*@exception AS400SecurityException If a security or authority error occurs.
*@exception ConnectionDroppedException If the connection is dropped
*unexpectedly.
*@exception IOException If an error occurs while communicating with the
*server.
*@exception InterruptedException If this thread is interrupted.
*@exception ServerStartupException If the host server cannot be started.
*@exception UnknownHostException If the server cannot be located.
**/
public void connect()
throws AS400SecurityException, ConnectionDroppedException, IOException,
InterruptedException, ServerStartupException, UnknownHostException
{
if (server_ == null)
{
server_ = system_.getConnection(AS400.RECORDACCESS,
false, /* force new connection */
false /* skip signon server */ ); //@C0C @B5C
}
}
//@D0M 7/15/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.
*@exception UnknownHostException If the server cannot be located.
**/
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) RCDLEN(92) MBR(*FILE) TEXT('JT400 TEMPORARY DDS SOURCE FILE')"); //@B0C @F0C
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) RCDLEN(92) MBR(*FILE) TEXT('JT400 TEMPORARY DDS SOURCE FILE')"); //@F0C
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"));
// - Can't use the system object here because we have no way of filling in the converter
srcRF.addFieldDescription(new CharacterFieldDescription(new AS400Text(80, system_.getCcsid()), "SRCDTA")); //@D0C
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.traceOn_)
{
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().
AS400FileImplRemote src = null; //@B5C @D0C 7/15/99
try
{
//@B5D src = new SequentialFile(system_, "/QSYS.LIB/QTEMP.LIB/JT400DSSRC.FILE");
//@B5 - Can't create a new AS400FileImplBase because it's abstract.
//@B5 - Other alternative is to create a new ImplRemote or
// ImplNative based on the type of this. But can't create
// a new AS400FileImplNative since it doesn't exist at
// compile time.
//@D0D 7/15/99 src = (AS400FileImplBase)this.clone(); //@B5A
//@B5D try
//@B5D {
//@B5D src.setRecordFormat(srcRF);
//@B5D }
//@B5D catch(PropertyVetoException e)
//@B5D { // This is to quiet the compiler
//@B5D }
src = new AS400FileImplRemote(); //@D0A 7/15/99
src.setAll(system_, "/QSYS.LIB/QTEMP.LIB/JT400DSSRC.FILE", srcRF, false, false, false); //@B5A
//@B5D src.open(AS400File.WRITE_ONLY, records.length, AS400File.COMMIT_LOCK_LEVEL_NONE);
src.openFile2(AS400File.WRITE_ONLY, records.length, AS400File.COMMIT_LOCK_LEVEL_NONE, false); //@B5A
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 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.
**/
public void deleteCurrentRecord()
throws AS400Exception,
AS400SecurityException,
InterruptedException,
IOException
{
connect();
// Send the delete record data stream request
Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38DEL(dclName_), newCorrelationId()); //@B6C
// Reply expected: S38IOFB
if (!(replys.size() == 1 && ((DDMDataStream)replys.elementAt(0)).getCodePoint() == DDMTerm.S38IOFB))
{
handleErrorReply(replys, 1);
}
}
/**
Executes a command on the server.
@param cmd the command
*@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.
**/
public AS400Message[] execute(String cmd)
throws AS400SecurityException, InterruptedException, IOException
{
connect();
// Send the submit command data stream request
Vector replys = null;
if (discardReplys_)
{
synchronized(server_) //@F1A
{ //@F1A
server_.sendAndDiscardReply(DDMRequestDataStream.getRequestS38CMD(cmd, system_),newCorrelationId()); //@C0C @M8C
} //@F1A
}
else
{
replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38CMD(cmd, system_), newCorrelationId()); //@C0C @B6C
}
return processReplys(replys);
}
/**
*Throws an appropriate exception based on the type of reply
*data stream specified.
*@param replys The replys to be checked to determine the error.
*@param index The index within replys at which to start.
*@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.
**/
public void handleErrorReply(Vector replys, int index)
throws AS400Exception,
AS400SecurityException,
InterruptedException,
IOException
{
int size = replys.size();
DDMDataStream reply;
int codePoint;
Vector as400MsgList = new Vector();
for (int i = index; i < size; ++i)
{
reply = (DDMDataStream)replys.elementAt(i);
codePoint = reply.getCodePoint();
switch (codePoint)
{
case DDMTerm.S38MSGRM:
// Because we will normally get more than one AS400Message for an
// error condition, we build a vector of AS400Message arrays and
// throw an AS400Exception with all messages once we have finished
// parsing the replies. Note that the DDMAS400MessageReply class
// extracts all the server messages contained in reply.data_. I.e.
// a single reply may contain more than one message.
DDMAS400MessageReply msgReply = new DDMAS400MessageReply(system_, reply.data_); //@C0C
as400MsgList.addElement(msgReply.getAS400MessageList());
break;
// If any of the following cases occur, we throw the exception and are done.
case DDMTerm.AGNPRMRM:
case DDMTerm.CMDCHKRM:
case DDMTerm.CMDNSPRM:
case DDMTerm.DCLNAMRM:
case DDMTerm.PRCCNVRM:
case DDMTerm.PRMNSPRM:
case DDMTerm.RSCLMTRM:
case DDMTerm.SYNTAXRM:
case DDMTerm.VALNSPRM:
Trace.log(Trace.ERROR, "handleErrorReply()", reply.data_);
throw new InternalErrorException(InternalErrorException.UNKNOWN, codePoint);
default:
// We don't know what the reply is. Throw exception and be done.
Trace.log(Trace.ERROR, "handleErrorReply()", reply.data_);
throw new InternalErrorException(InternalErrorException.DATA_STREAM_UNKNOWN, codePoint);
}
}
// If we get to here, we should have a list of messages to throw
if (as400MsgList.size() > 0)
{
// We need to expand out the Vector of AS400Message[]'s to individual
// AS400Message's in a single array in order to construct our AS400Exception.
int numberOfMessages = 0;
int msgListSize = as400MsgList.size();
int msgNumber = 0;
// Determine number of AS400Message objects we have
for (int i = 0; i < msgListSize; ++i)
{
numberOfMessages += ((AS400Message[])as400MsgList.elementAt(i)).length;
}
// Now populate the single AS400Message[] with the AS400Message objects
AS400Message[] msgs = new AS400Message[numberOfMessages];
AS400Message[] m;
for (int i = 0; i < msgListSize; ++i)
{
m = (AS400Message[])as400MsgList.elementAt(i);
for (int j = 0; j < m.length; ++j)
{
msgs[msgNumber++] = m[j];
}
}
throw new AS400Exception(msgs);
}
Trace.log(Trace.ERROR, "No messages from server.");
throw new InternalErrorException(InternalErrorException.DATA_STREAM_UNKNOWN);
}
/**
*Logs warning messages if tracing is on. This method should be used to log
*warning messages (trace.WARNING) when an operation is successful yet additional
*AS400 messages are sent with the reply.
*@param v The vector of replies containing messages to log.
*@param index The index into v at which to stop pulling out messages.
*@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.
**/
public void logWarningMessages(Vector v, int index)
throws AS400Exception,
AS400SecurityException,
InterruptedException,
IOException
{
if (Trace.traceOn_)
{
DDMAS400MessageReply msg;
for (int i = 0; i < index; ++i)
{
msg = new DDMAS400MessageReply(system_, ((DDMDataStream)v.elementAt(i)).data_); //@C0C
AS400Message[] msgs = msg.getAS400MessageList();
Trace.log(Trace.WARNING, "AS400FileImplRemote.logWarningMessages():");
for (int j = 0; j < msgs.length; ++j)
{
Trace.log(Trace.WARNING, msgs[j].toString());
}
}
}
}
/**
*Looks for a particular code point in a vector of data streams.
*@param cp The code point for which to look.
*@param v The data streams in which to look.
*@return The index of the datastream in v in which the code point was found.
*If the code point is not found, -1 is returned.
**/
private int lookForCodePoint(int cp, Vector v)
{
int index = -1;
int size = v.size();
DDMDataStream ds;
int offset;
// Search each datastream in v for the code point.
for (int i = 0; i < size && index == -1; ++i)
{
ds = (DDMDataStream)v.elementAt(i);
offset = 8;
// Search through all the code points in an element of v for cp.
while (index == -1 && offset < ds.data_.length)
{
if (ds.get16bit(offset) == cp)
{ // Found the code point
index = i;
}
else
{ // Move on to the next code point
offset += ds.get16bit(offset - 2);
}
}
}
return index;
}
/**
*Opens the file. Helper function to open file for keyed or
*sequential files.
*@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..
*@exception UnknownHostException If the server cannot be located.
**/
public DDMS38OpenFeedback openFile(int openType, int bf, String access)
throws AS400Exception,
AS400SecurityException,
InterruptedException,
IOException
{
// Ensure that we are connected to the server.
connect();
// Create the user file control block.
byte[] ufcb = createUFCB(openType, bf, access, false);
// Send the open file data stream request
Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38OPEN(ufcb, dclName_), newCorrelationId()); //@B6C
// Reply expected: S38OPNFB
int index = lookForCodePoint(DDMTerm.S38OPNFB, replys);
DDMDataStream reply;
if (index != -1)
{
reply = (DDMDataStream)replys.elementAt(index);
openFeedback_ = new DDMS38OpenFeedback(system_, reply.data_); //@C0C
if (Trace.traceOn_)
{
Trace.log(Trace.INFORMATION, "AS400FileImplRemote.openFile()\n" + openFeedback_.toString());
}
if (index != 0)
{ // Informational and/or diagnostic messages were issued. Log them.
logWarningMessages(replys, index);
}
}
else
{
handleErrorReply(replys, 0);
}
return openFeedback_;
}
/**
*Pads data with padByte numBytes starting at start.
*@param data The data to pad.
*@param start The offset in data at which we begin padding.
*@param numBytes The number of bytes to pad.
*@param padChar The character with which to pad.
**/
public void padBytes(byte[] data, int start, int numBytes, byte padChar)
{
for (int i = 0; i < numBytes; ++i)
{
data[start + i] = padChar;
}
}
/**
*Positions the cursor for the file. Which record to position the cursor to is
*determined by the type
*argument.
*@param searchType The type of get to execute. Valid values are:
*
*- TYPE_GET_FIRST
*
- TYPE_GET_NEXT
*
- TYPE_GET_LAST
*
- TYPE_GET_PREV
*
- TYPE_GET_SAME
*@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.
**/
public Record[] positionCursorAt(int searchType)
throws AS400Exception,
AS400SecurityException,
InterruptedException,
IOException
{
int shr; // Type of locking for the record
// @A1C
if ((openType_ == AS400File.READ_ONLY) || //@C0C
((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) // @A1A //@C0C
{
// Read only
shr = SHR_READ_NORM;
}
else
{ // READ_WRITE, lock the record for update
shr = SHR_UPD_NORM;
}
// Send the request to read. Ignore the data; don't specify
// DATA_NODTA_DTARCD because it is one of the most inefficient
// paths per the DDM server guys. More efficient to get the data and ignore
// it they say.
Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GET(dclName_, searchType, shr, DATA_DTA_DTARCD), newCorrelationId()); //@B6C
int codePoint = ((DDMDataStream)replys.elementAt(0)).getCodePoint();
if (codePoint == DDMTerm.S38IOFB && replys.size() > 1)
{
handleErrorReply(replys, 1);
}
else if (codePoint != DDMTerm.S38BUF)
{
handleErrorReply(replys, 0);
}
Record[] returned = processReadReply(replys, false); // @A1C
return returned;
}
//@RBA
public Record[] positionCursorAtLong(int searchType)
throws AS400Exception,
AS400SecurityException,
InterruptedException,
IOException
{
int shr; // Type of locking for the record
// @A1C
if ((openType_ == AS400File.READ_ONLY) || //@C0C
((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) // @A1A //@C0C
{
// Read only
shr = SHR_READ_NORM;
}
else
{ // READ_WRITE, lock the record for update
shr = SHR_UPD_NORM;
}
// Send the request to read. Ignore the data; don't specify
// DATA_NODTA_DTARCD because it is one of the most inefficient
// paths per the DDM server guys. More efficient to get the data and ignore
// it they say.
Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GET(dclName_, searchType, shr, DATA_DTA_DTARCD), newCorrelationId()); //@B6C
int codePoint = ((DDMDataStream)replys.elementAt(0)).getCodePoint();
if (codePoint == DDMTerm.S38IOFB && replys.size() > 1)
{
handleErrorReply(replys, 1);
}
else if (codePoint != DDMTerm.S38BUF)
{
handleErrorReply(replys, 0);
}
Record[] returned = processReadReplyLong(replys, false); // @A1C
return returned;
}
/**
*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 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.
**/
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();
}
connect();
// Send the request to force end of data specifying end of the file for
// positioning.
// Need correlation id as we will be chaining an S38BUF object to this request
// and the ids must match.
int id = newCorrelationId(); //@B6C
DDMRequestDataStream req = DDMRequestDataStream.getRequestS38FEOD(dclName_, TYPE_GET_LAST, SHR_READ_NORM_RLS, DATA_NODTA_DTARCD);
req.setIsChained(true); // We will be chaining an S38BUF to this request
req.setHasSameRequestCorrelation(true); // When chaining, must indicate
// that the correlation ids are
// the same.
Vector replys = null;
try
{
synchronized(server_) //@F1A - both datastreams must be written atomically
{ //@F1A
server_.send(req, id); // Send the S38FEOD request
// Although the S38FEOD term description states that
// the S38BUF object is optional, it is not for our purposes.
// Create an empty S38BUF object to send after the S38FEOD
DDMObjectDataStream s38buf = new DDMObjectDataStream(11);
// The class contains static variable s38Buffer which is the S38BUF term with
// a value of 1 byte set to 0x00.
System.arraycopy(s38Buffer, 0, s38buf.data_, 6, 5);
// Send the S38BUF
replys = sendRequestAndReceiveReplies(s38buf, id);
} //@F1A
}
catch (ConnectionDroppedException e)
{
// Connection dropped. Disconnect server and rethrow.
Trace.log(Trace.ERROR, "ConnectionDroppedException:", e);
system_.disconnectServer(server_); //@C0C
//@C1 - Setting the server_ object to null means that
// any operations on this AS400File object after the connection has been
// dropped will result in a NullPointerException. By leaving the server_ object
// around, any subsequent operations should also throw a ConnectionDroppedException.
//@C1D server_ = null;
resetState();
throw e;
}
// Reply expected: S38IOFB, which may be followed by an S38MSGRM
// with id CPF5001 which indicates that the end of file was reached.
// Any other S38MSGRM replies indicate that an error has occurred.
if (((DDMDataStream)replys.elementAt(0)).getCodePoint() == DDMTerm.S38IOFB)
{
if (replys.size() != 1)
{
if (!verifyS38MSGRM((DDMDataStream)replys.elementAt(1), "CPF5001", 0))
{
handleErrorReply(replys, 1);
}
}
}
else
{
handleErrorReply(replys, 0);
}
}
/**
*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 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.
**/
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();
}
connect();
// Send the request to force end of data specifying beginning of the
// file for positioning; this is how we position the cursor to
// before the first record.
// Need correlation id as we will be chaining an S38BUF object to this request
// and the ids must match.
int id = newCorrelationId(); //@B6C
DDMRequestDataStream req = DDMRequestDataStream.getRequestS38FEOD(dclName_, TYPE_GET_FIRST, SHR_READ_NORM_RLS, DATA_NODTA_DTARCD);
req.setIsChained(true); // We will be chaining an S38BUF to this request
req.setHasSameRequestCorrelation(true); // When chaining, must indicate
// that the correlation ids are
// the same.
Vector replys = null;
try
{
synchronized(server_) //@F1A - both datastreams must be written atomically
{ //@F1A
server_.send(req, id); // Send the S38FEOD request
// Although the S38FEOD term description states that
// the S38BUF object is optional, it is not for our purposes.
// Create an empty S38BUF object to send after the S38FEOD
DDMObjectDataStream s38buf = new DDMObjectDataStream(11);
// The class contains static variable s38Buffer which is the S38BUF term with
// a value of 1 byte set to 0x00.
System.arraycopy(s38Buffer, 0, s38buf.data_, 6, 5);
// Send the S38BUF
replys = sendRequestAndReceiveReplies(s38buf, id);
} //@F1A
}
catch (ConnectionDroppedException e)
{
// Connection dropped. Disconnect server and rethrow.
Trace.log(Trace.ERROR, "ConnectionDroppedException:", e);
system_.disconnectServer(server_); //@C0C
//@C1 - Setting the server_ object to null means that
// any operations on this AS400File object after the connection has been
// dropped will result in a NullPointerException. By leaving the server_ object
// around, any subsequent operations should also throw a ConnectionDroppedException.
//@C1D server_ = null;
resetState();
throw e;
}
// Reply expected: S38IOFB, which may be followed by an S38MSGRM
// with id CPF5001 which indicates that the end of file was reached.
// Any other S38MSGRM replies indicate that an error has occurred.
if (((DDMDataStream)replys.elementAt(0)).getCodePoint() == DDMTerm.S38IOFB)
{
if (replys.size() != 1)
{
if (!verifyS38MSGRM((DDMDataStream)replys.elementAt(1), "CPF5001", 0))
{
handleErrorReply(replys, 1);
}
}
}
else
{
handleErrorReply(replys, 0);
}
}
/**
*Positions the file cursor to the specified record number in the file on the server.
*@param recordNumber The record number to which to position the file cursor.
*@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.
**/
public Record positionCursorToIndex(int recordNumber)
throws AS400Exception,
AS400SecurityException,
InterruptedException,
IOException
{
int shr; // Type of locking for the record
if (cacheRecords_ && !cache_.setPosition(recordNumber)) //@G0A
{
// Invalidate the cache
cache_.setIsEmpty(); //@G0A
}
// @A1C
if ((openType_ == AS400File.READ_ONLY) || //@C0C
((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) // @A1A //@C0C
{
// Read only
shr = SHR_READ_NORM;
}
else
{ // READ_WRITE; get for update
shr = SHR_UPD_NORM;
}
// Send the request to read. Specify 0x08 for type
// to indicate that the record number is determined from the start of the file.
// Note that even though we are only positioning the cursor, we must specify
// DATA_DTA_DTARCD which causes the record to be retrieved. This is because
// the cursor will not be positioned properly if we do not actually get the
// record. This situation occurs when accessing by record number or by key but
// not when accessing sequentially.
// Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GETD(dclName_, recordFormat_, 0x08, shr, DATA_DTA_DTARCD, recordNumber, system_), server_.newCorrelationId()); // @A1D //@C0C
Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GETD(dclName_, recordFormatCTLLName_, 0x08, shr, DATA_DTA_DTARCD, recordNumber, system_), newCorrelationId()); // @A1A //@C0C @B6C
// Reply expected: S38BUF
int codePoint = ((DDMDataStream)replys.elementAt(0)).getCodePoint();
if (codePoint == DDMTerm.S38IOFB && replys.size() > 1)
{
handleErrorReply(replys, 1);
}
else if (codePoint != DDMTerm.S38BUF)
{
handleErrorReply(replys, 0);
}
Record[] returned = processReadReply(replys, false); // @A1C
return returned[0]; // @A1A
// return null; // @A1D
}
//@RBA
public Record positionCursorToIndexLong(long recordNumber)
throws AS400Exception,
AS400SecurityException,
InterruptedException,
IOException
{
int shr; // Type of locking for the record
if (cacheRecords_ && !cache_.setPositionLong(recordNumber)) //@G0A
{
// Invalidate the cache
cache_.setIsEmpty(); //@G0A
}
// @A1C
if ((openType_ == AS400File.READ_ONLY) || //@C0C
((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) // @A1A //@C0C
{
// Read only
shr = SHR_READ_NORM;
}
else
{ // READ_WRITE; get for update
shr = SHR_UPD_NORM;
}
// Send the request to read. Specify 0x08 for type
// to indicate that the record number is determined from the start of the file.
// Note that even though we are only positioning the cursor, we must specify
// DATA_DTA_DTARCD which causes the record to be retrieved. This is because
// the cursor will not be positioned properly if we do not actually get the
// record. This situation occurs when accessing by record number or by key but
// not when accessing sequentially.
// Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GETD(dclName_, recordFormat_, 0x08, shr, DATA_DTA_DTARCD, recordNumber, system_), server_.newCorrelationId()); // @A1D //@C0C
Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GETDLong(dclName_, recordFormatCTLLName_, 0x08, shr, DATA_DTA_DTARCD, recordNumber, system_), newCorrelationId()); // @A1A //@C0C @B6C
// Reply expected: S38BUF
int codePoint = ((DDMDataStream)replys.elementAt(0)).getCodePoint();
if (codePoint == DDMTerm.S38IOFB && replys.size() > 1)
{
handleErrorReply(replys, 1);
}
else if (codePoint != DDMTerm.S38BUF)
{
handleErrorReply(replys, 0);
}
Record[] returned = processReadReplyLong(replys, false); // @A1C
return returned[0]; // @A1A
// return null; // @A1D
}
/**
*Positions the cursor to the first record with the specified key based on the specified
*type of read.
*@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.
*@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.
**/
public Record positionCursorToKey(Object[] key, int searchType)
throws AS400Exception,
AS400SecurityException,
InterruptedException,
IOException
{
int shr; // Type of locking for the record
if (cacheRecords_) //@G0A
{
// Invalidate the cache
cache_.setIsEmpty(); //@G0A
}
// @A1C
if ((openType_ == AS400File.READ_ONLY) || //@C0C
((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) // @A1A //@C0C
{
// Read only
shr = SHR_READ_NORM;
}
else
{ // READ_WRITE
shr = SHR_UPD_NORM;
}
// In order to have the file cursor remain properly positioned, we specify that the record
// is to be returned on the GETK request as opposed to specifying DATA_NODTA_DTARCD. This
// is necessary for the caching support.
// Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GETK(dclName_, recordFormat_, type, shr, DATA_DTA_DTARCD, key, system_), server_.newCorrelationId()); // @A1D
Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GETK(dclName_, recordFormat_, recordFormatCTLLName_, searchType, shr, DATA_DTA_DTARCD, key, system_), newCorrelationId()); // @A1A @B6C
int codePoint = ((DDMDataStream)replys.elementAt(0)).getCodePoint();
if (codePoint == DDMTerm.S38IOFB && replys.size() > 1)
{
handleErrorReply(replys, 1);
}
else if (codePoint != DDMTerm.S38BUF)
{
handleErrorReply(replys, 0);
}
// Record[] returned = processReadReply(replys, true); // @A1C
// return returned[0]; // @A1D
processReadReply(replys, true); // @A1C
return null; // @A1A
}
//@RBA
public Record positionCursorToKeyLong(Object[] key, int searchType)
throws AS400Exception,
AS400SecurityException,
InterruptedException,
IOException
{
int shr; // Type of locking for the record
if (cacheRecords_) //@G0A
{
// Invalidate the cache
cache_.setIsEmpty(); //@G0A
}
// @A1C
if ((openType_ == AS400File.READ_ONLY) || //@C0C
((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) // @A1A //@C0C
{
// Read only
shr = SHR_READ_NORM;
}
else
{ // READ_WRITE
shr = SHR_UPD_NORM;
}
// In order to have the file cursor remain properly positioned, we specify that the record
// is to be returned on the GETK request as opposed to specifying DATA_NODTA_DTARCD. This
// is necessary for the caching support.
// Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GETK(dclName_, recordFormat_, type, shr, DATA_DTA_DTARCD, key, system_), server_.newCorrelationId()); // @A1D
Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GETK(dclName_, recordFormat_, recordFormatCTLLName_, searchType, shr, DATA_DTA_DTARCD, key, system_), newCorrelationId()); // @A1A @B6C
int codePoint = ((DDMDataStream)replys.elementAt(0)).getCodePoint();
if (codePoint == DDMTerm.S38IOFB && replys.size() > 1)
{
handleErrorReply(replys, 1);
}
else if (codePoint != DDMTerm.S38BUF)
{
handleErrorReply(replys, 0);
}
// Record[] returned = processReadReply(replys, true); // @A1C
// return returned[0]; // @A1D
processReadReplyLong(replys, true); // @A1C
return null; // @A1A
}
// @A1A
/**
*Positions the cursor to the first record with the specified key based on the specified
*type of read.
*@param key The byte array that contains the byte 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.
*@param numberOfKeyFields The number of key fields contained in the byte array key.
*@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.
**/
public Record positionCursorToKey(byte[] key, int searchType, int numberOfKeyFields)
throws AS400Exception,
AS400SecurityException,
InterruptedException,
IOException
{
int shr; // Type of locking for the record
if (cacheRecords_) //@G0A
{
// Invalidate the cache
cache_.setIsEmpty(); //@G0A
}
if ((openType_ == AS400File.READ_ONLY) ||
((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) // @A1A
{
// Read only
shr = SHR_READ_NORM;
}
else
{ // READ_WRITE
shr = SHR_UPD_NORM;
}
// In order to have the file cursor remain properly positioned, we specify that the record
// is to be returned on the GETK request as opposed to specifying DATA_NODTA_DTARCD. This
// is necessary for the caching support.
// Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GETK(dclName_, recordFormat_, type, shr, DATA_DTA_DTARCD, key, system_, numberOfKeyFields), server_.newCorrelationId()); // @A1D
Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GETK(dclName_, recordFormatCTLLName_, searchType, shr, DATA_DTA_DTARCD, key, system_, numberOfKeyFields), newCorrelationId()); // @A1A @B6C
int codePoint = ((DDMDataStream)replys.elementAt(0)).getCodePoint();
if (codePoint == DDMTerm.S38IOFB && replys.size() > 1)
{
handleErrorReply(replys, 1);
}
else if (codePoint != DDMTerm.S38BUF)
{
handleErrorReply(replys, 0);
}
// Record[] returned = processReadReply(replys, true); // @A1C
// return returned[0]; // @A1D
processReadReply(replys, true); // @A1C
return null; // @A1A
}
//@RBA Add this to support long time record number
public Record positionCursorToKeyLong(byte[] key, int searchType, int numberOfKeyFields)
throws AS400Exception,
AS400SecurityException,
InterruptedException,
IOException
{
int shr; // Type of locking for the record
if (cacheRecords_) //@G0A
{
// Invalidate the cache
cache_.setIsEmpty(); //@G0A
}
if ((openType_ == AS400File.READ_ONLY) ||
((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) // @A1A
{
// Read only
shr = SHR_READ_NORM;
}
else
{ // READ_WRITE
shr = SHR_UPD_NORM;
}
// In order to have the file cursor remain properly positioned, we specify that the record
// is to be returned on the GETK request as opposed to specifying DATA_NODTA_DTARCD. This
// is necessary for the caching support.
// Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GETK(dclName_, recordFormat_, type, shr, DATA_DTA_DTARCD, key, system_, numberOfKeyFields), server_.newCorrelationId()); // @A1D
Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GETK(dclName_, recordFormatCTLLName_, searchType, shr, DATA_DTA_DTARCD, key, system_, numberOfKeyFields), newCorrelationId()); // @A1A @B6C
int codePoint = ((DDMDataStream)replys.elementAt(0)).getCodePoint();
if (codePoint == DDMTerm.S38IOFB && replys.size() > 1)
{
handleErrorReply(replys, 1);
}
else if (codePoint != DDMTerm.S38BUF)
{
handleErrorReply(replys, 0);
}
// Record[] returned = processReadReply(replys, true); // @A1C
// return returned[0]; // @A1D
processReadReplyLong(replys, true); // @A1C
return null; // @A1A
}
/**
*Processes the replys vector for records read. Throws exceptions
*in error cases.
*@param replys The reply datastream(s) containing the record(s) read.
* @param discardRecords should records be discarded
*@return The records read from the system. Returns null if no records were
*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.
**/
// Record[] processReadReply(Vector replys) // @A1D
public Record[] processReadReply(Vector replys, boolean discardRecords) // @A1A
throws AS400Exception,
AS400SecurityException,
InterruptedException,
IOException
{
Record[] returned = null; // Will contain the records read.
int recordIncrement = openFeedback_.getRecordIncrement();
// The format of the reply(s) should be one or more S38BUF objects
// followed by an S38IOFB. However if the end of file was reached or
// if the record to be read was not found in the file, an S38IOFB followed
// be an S38MSGRM indicating CPF5006 or CPF5001 will be returned. If this is
// the case, we return null instead of throwing an exception.
// If an error occurs we may get an S38IOFB followed by S38MSGRM objects indicating
// the server errors that occurred. In that case we throw an exception via
// handleErrorReply. If we only get an S38IOFB back, we also throw an
// exception as an error must have occurred. This case should not happen.
int codePoint = ((DDMDataStream)replys.elementAt(0)).getCodePoint();
if (codePoint == DDMTerm.S38IOFB)
{ // The end of file was reached, the record to be read was not found or an
// error occurred.
if (replys.size() > 1)
{
codePoint = ((DDMDataStream)replys.elementAt(1)).getCodePoint();
if (codePoint == DDMTerm.S38MSGRM)
{ // Check for end of file or record not found messages
DDMAS400MessageReply err = new DDMAS400MessageReply(system_, ((DDMDataStream)replys.elementAt(1)).data_);
String msgId = err.getAS400Message().getID();
if (msgId.equals("CPF5006") || msgId.equals("CPF5001"))
{ // End of file reached or record not found; return null record
return returned;
}
else
{ // Error occurred
handleErrorReply(replys, 1);
}
}
else
{ // Some other error (other than an error) occurred
handleErrorReply(replys, 1);
}
}
else
{ // Only an S38IOFB object was returned. Error situation. Should not occur.
handleErrorReply(replys, 0);
}
}
else if (codePoint == DDMTerm.S38BUF)
{
if (discardRecords)
{ // @A1A
return returned; // @A1A
} // @A1A
// Records were read. Extract format them
// Extract the returned records and the io feedback info
// The S38IOFB will be the last object in the reply(s)
DDMDataStream reply = (DDMDataStream)replys.elementAt(0);
DDMS38IOFB ioFeedback;
boolean largeBuffer;
// If the length of the S38BUF term is greater than 0x7FFF, there
// will be an extra 4 bytes after the S38BUF code point which indicate the length of
// the S38BUF data. We need to special handle these instances, so we use largeBuffer
// to indicate when such a case has occurred.
largeBuffer = (reply.get16bit(6) <= 0x7FFF)? false : true;
if (reply.isChained())
{ // The IO feedback is in the next reply. The S38IOFB data starts at offset
// 10 in the next reply.
ioFeedback = new DDMS38IOFB(((DDMDataStream)replys.elementAt(1)).data_, 10);
}
else
{ // The io feedback info is in this reply.
// if (largeBuffer)
// The length of the record data is in the 4 bytes following the S38BUF
// code point. The S38IOFB data will start at that length + 6 for the header,
// + 8 for the S38BUF LL-CP-extra LL, + 4 for the S38IOFB LL-CP.
// else
// The length of the record data is contained in the LL preceding
// the S38BUF codepoint. The S38IOFB data will start at that length + 6
// for the header, + 4 for the LL-CP of the S38BUF, + 4 for the S38IOFB LL-CP.
int offset = (largeBuffer)? reply.get32bit(10) + 18 : reply.get16bit(6) + 10;
ioFeedback = new DDMS38IOFB(reply.data_, offset);
}
// Extract the record(s) returned; because we are in the if (S38BUF code point found)
// code, we know that there was at least one record returned.
// The S38IOFB contains the number of records read.
int numberOfRecords = ioFeedback.getNumberOfRecordsReturned();
returned = new Record[numberOfRecords];
// if (largeBuffer), the S38BUF CP is followed by 4 bytes of record length info,
// then the record data; otherwise the record data follows the code point
int recordOffset = (largeBuffer)? 14 : 10;
// Determine the offset of the record number within the S38BUF for each record.
// The S38IOFB contains the record length for the record(s). The record length
// consists of the length of the data plus a variable size gap plus the null byte
// field map. The record number offset is 2 bytes more than the record length.
int recordNumberOffset = recordOffset + ioFeedback.getRecordLength() + 2;
// 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 = recordOffset + openFeedback_.getNullFieldByteMapOffset();
// Determine the number of fields in a record from the RecordFormat for this file.
int numFields = recordFormat_.getNumberOfFields();
// 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)
{ // Extract the records from the datastream reply.
returned[i] = recordFormat_.getNewRecord(reply.data_, recordOffset + i * recordIncrement);
// Set any null fields to null
if (isNullCapable)
{ // File has null capable fields
for (int j = 0; j < numFields; ++j)
{ // 0xF1 = field is null, 0xF0 = field is not null
if (reply.data_[nullFieldMapOffset + j + i * recordIncrement] == (byte)0xF1)
{
returned[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
{
returned[i].setRecordNumber(BinaryConverter.byteArrayToInt(reply.data_, recordNumberOffset + i * recordIncrement));
}
catch (PropertyVetoException e)
{ // We created the Record objects. There is no one to veto anything
} // so this is here to quit the compiler
}
}
else
{ // Error occurred
handleErrorReply(replys, 0);
}
return returned;
}
//@RBA
public Record[] processReadReplyLong(Vector replys, boolean discardRecords) // @A1A
throws AS400Exception,
AS400SecurityException,
InterruptedException,
IOException
{
Record[] returned = null; // Will contain the records read.
int recordIncrement = openFeedback_.getRecordIncrement();
// The format of the reply(s) should be one or more S38BUF objects
// followed by an S38IOFB. However if the end of file was reached or
// if the record to be read was not found in the file, an S38IOFB followed
// be an S38MSGRM indicating CPF5006 or CPF5001 will be returned. If this is
// the case, we return null instead of throwing an exception.
// If an error occurs we may get an S38IOFB followed by S38MSGRM objects indicating
// the server errors that occurred. In that case we throw an exception via
// handleErrorReply. If we only get an S38IOFB back, we also throw an
// exception as an error must have occurred. This case should not happen.
int codePoint = ((DDMDataStream)replys.elementAt(0)).getCodePoint();
if (codePoint == DDMTerm.S38IOFB)
{ // The end of file was reached, the record to be read was not found or an
// error occurred.
if (replys.size() > 1)
{
codePoint = ((DDMDataStream)replys.elementAt(1)).getCodePoint();
if (codePoint == DDMTerm.S38MSGRM)
{ // Check for end of file or record not found messages
DDMAS400MessageReply err = new DDMAS400MessageReply(system_, ((DDMDataStream)replys.elementAt(1)).data_);
String msgId = err.getAS400Message().getID();
if (msgId.equals("CPF5006") || msgId.equals("CPF5001"))
{ // End of file reached or record not found; return null record
return returned;
}
else
{ // Error occurred
handleErrorReply(replys, 1);
}
}
else
{ // Some other error (other than an error) occurred
handleErrorReply(replys, 1);
}
}
else
{ // Only an S38IOFB object was returned. Error situation. Should not occur.
handleErrorReply(replys, 0);
}
}
else if (codePoint == DDMTerm.S38BUF)
{
if (discardRecords)
{ // @A1A
return returned; // @A1A
} // @A1A
// Records were read. Extract format them
// Extract the returned records and the io feedback info
// The S38IOFB will be the last object in the reply(s)
DDMDataStream reply = (DDMDataStream)replys.elementAt(0);
DDMS38IOFB ioFeedback;
boolean largeBuffer;
// If the length of the S38BUF term is greater than 0x7FFF, there
// will be an extra 4 bytes after the S38BUF code point which indicate the length of
// the S38BUF data. We need to special handle these instances, so we use largeBuffer
// to indicate when such a case has occurred.
largeBuffer = (reply.get16bit(6) <= 0x7FFF)? false : true;
if (reply.isChained())
{ // The IO feedback is in the next reply. The S38IOFB data starts at offset
// 10 in the next reply.
ioFeedback = new DDMS38IOFB(((DDMDataStream)replys.elementAt(1)).data_, 10);
}
else
{ // The io feedback info is in this reply.
// if (largeBuffer)
// The length of the record data is in the 4 bytes following the S38BUF
// code point. The S38IOFB data will start at that length + 6 for the header,
// + 8 for the S38BUF LL-CP-extra LL, + 4 for the S38IOFB LL-CP.
// else
// The length of the record data is contained in the LL preceding
// the S38BUF codepoint. The S38IOFB data will start at that length + 6
// for the header, + 4 for the LL-CP of the S38BUF, + 4 for the S38IOFB LL-CP.
int offset = (largeBuffer)? reply.get32bit(10) + 18 : reply.get16bit(6) + 10;
ioFeedback = new DDMS38IOFB(reply.data_, offset);
}
// Extract the record(s) returned; because we are in the if (S38BUF code point found)
// code, we know that there was at least one record returned.
// The S38IOFB contains the number of records read.
int numberOfRecords = ioFeedback.getNumberOfRecordsReturned();
returned = new Record[numberOfRecords];
// if (largeBuffer), the S38BUF CP is followed by 4 bytes of record length info,
// then the record data; otherwise the record data follows the code point
int recordOffset = (largeBuffer)? 14 : 10;
// Determine the offset of the record number within the S38BUF for each record.
// The S38IOFB contains the record length for the record(s). The record length
// consists of the length of the data plus a variable size gap plus the null byte
// field map. The record number offset is 2 bytes more than the record length.
int recordNumberOffset = recordOffset + ioFeedback.getRecordLength() + 2;
// 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 = recordOffset + openFeedback_.getNullFieldByteMapOffset();
// Determine the number of fields in a record from the RecordFormat for this file.
int numFields = recordFormat_.getNumberOfFields();
// 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)
{ // Extract the records from the datastream reply.
returned[i] = recordFormat_.getNewRecord(reply.data_, recordOffset + i * recordIncrement);
// Set any null fields to null
if (isNullCapable)
{ // File has null capable fields
for (int j = 0; j < numFields; ++j)
{ // 0xF1 = field is null, 0xF0 = field is not null
if (reply.data_[nullFieldMapOffset + j + i * recordIncrement] == (byte)0xF1)
{
returned[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
{
returned[i].setRecordNumberLong(BinaryConverter.byteArrayToUnsignedInt(reply.data_, recordNumberOffset + i * recordIncrement));
}
catch (PropertyVetoException e)
{ // We created the Record objects. There is no one to veto anything
} // so this is here to quit the compiler
}
}
else
{ // Error occurred
handleErrorReply(replys, 0);
}
return returned;
}
/**
Process replys.
@param replys the replys from a request.
* @return array AS400Messages associated with the replies
*@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[] processReplys(Vector replys)
throws AS400SecurityException, InterruptedException, IOException
{
AS400Message[] msgs = null;
Vector as400MsgList = new Vector();
if (replys != null)
{
for (int i = 0; i < replys.size(); ++i)
{
DDMDataStream reply = (DDMDataStream)replys.elementAt(i);
int codePoint = reply.getCodePoint();
switch (codePoint)
{
case DDMTerm.S38MSGRM:
// Because we will normally get more than one AS400Message for an
// error condition, we build a vector of AS400Message arrays and
// throw an AS400Exception with all messages once we have finished
// parsing the replies. Note that the DDMAS400MessageReply class
// extracts all the server messages contained in reply.data_. I.e.
// a single reply may contain more than one message.
DDMAS400MessageReply msgReply = new DDMAS400MessageReply(system_, reply.data_);
as400MsgList.addElement(msgReply.getAS400MessageList());
break;
// If any of the following cases occur, we throw the exception and are done.
case DDMTerm.AGNPRMRM:
case DDMTerm.CMDCHKRM:
case DDMTerm.CMDNSPRM:
case DDMTerm.DCLNAMRM:
case DDMTerm.PRCCNVRM:
case DDMTerm.PRMNSPRM:
case DDMTerm.RSCLMTRM:
case DDMTerm.SYNTAXRM:
case DDMTerm.VALNSPRM:
Trace.log(Trace.ERROR, "handleErrorReply()", reply.data_);
throw new InternalErrorException(InternalErrorException.UNKNOWN, codePoint);
default:
// We don't know what the reply is. Throw exception and be done.
Trace.log(Trace.ERROR, "handleErrorReply()", reply.data_);
throw new InternalErrorException(InternalErrorException.DATA_STREAM_UNKNOWN, codePoint);
}
}
}
// If we get to here, we should have a list of messages to throw
if (as400MsgList.size() > 0)
{
// We need to expand out the Vector of AS400Message[]'s to individual
// AS400Message's in a single array in order to construct our AS400Exception.
int numberOfMessages = 0;
int msgListSize = as400MsgList.size();
int msgNumber = 0;
// Determine number of AS400Message objects we have
for (int i = 0; i < msgListSize; ++i)
{
numberOfMessages += ((AS400Message[])as400MsgList.elementAt(i)).length;
}
// Now populate the single AS400Message[] with the AS400Message objects
msgs = new AS400Message[numberOfMessages];
AS400Message[] m;
for (int i = 0; i < msgListSize; ++i)
{
m = (AS400Message[])as400MsgList.elementAt(i);
for (int j = 0; j < m.length; ++j)
{
msgs[msgNumber++] = m[j];
}
}
}
return (msgs == null ? new AS400Message[0] : msgs);
}
/**
*Reads the record with the specified record number.
*@param recordNumber The record number of the record to be read. The
*recordNumber must be greater than zero.
*@return The record read. If the record is not found, null is returned.
*@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.
**/
public Record read(int recordNumber)
throws AS400Exception,
AS400SecurityException,
InterruptedException,
IOException
{
if (cacheRecords_) //@C0A
return super.read(recordNumber); //@C0A
int shr; // Type of locking for the record
// @A1C
if ((openType_ == AS400File.READ_ONLY) ||
((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) // @A1A
{
// Read only
shr = SHR_READ_NORM;
}
else
{ // READ_WRITE
shr = SHR_UPD_NORM;
}
// Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GETD(dclName_, recordFormat_, 0x08, shr, DATA_DTA_DTARCD,recordNumber, // @A1D
Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GETD(dclName_, recordFormatCTLLName_, 0x08, shr, DATA_DTA_DTARCD,recordNumber, system_), newCorrelationId()); // @A1A @B6C
Record[] returned = processReadReply(replys, false); // @A1C
return(returned == null)? null : returned[0];
}
/**
*Reads all the records in the file. Helper function.
*@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 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.
*@exception UnknownHostException If the server cannot be located.
**/
public Record[] readAll(String fileType, int bf) //@D0C
throws AS400Exception,
AS400SecurityException,
InterruptedException,
IOException
{
//@B0A: Changed readAll() to not use the ULDRECF codepoint in the DDM data stream.
// This is because ULDRECF (Unload all records from file) does not handle
// null field values in records. (It throws us a data mapping error on
// the data stream, which would then cause an OutOfMemory exception
// or other strange errors in our code, because the data stream format
// was no longer correct.)
// readAll() now just uses the S38 extensions. It gets the first record
// and then gets the next record in a loop until null is returned.
// Using the S38 extensions gives better performance anyway (supposedly).
// See SequentialFile.readAll() and KeyedFile.readAll() for other changes.
// Other changes were also made in DDMRequestDataStream and DDMTerm.
//@B0A: start block
// readAll is supposed to return at least a Record[] of size 0, never null
Record[] recArray = new Record[0];
synchronized(this) // We synchronize because this file object
{ // isn't supposed to be open (as far as the user knows).
// Use a calculated blocking factor, else use a large blocking factor
//@D0M int bf = 2048/(recordFormat_.getNewRecord().getRecordLength() + 16); //@D0C
//@D0M if (bf <= 0) bf = 1; //@D0C
//@E0 - We don't want to use COMMIT_LOCK_LEVEL_ALL in case commitment control is on because
// inside readAll(), the file isn't supposed to be open, so we should treat it as such.
openFile2(AS400File.READ_ONLY, bf, AS400File.COMMIT_LOCK_LEVEL_NONE, fileType); //@D0C @E0C
// The vector to hold the records as we retrieve them.
Vector allRecords = new Vector();
// The following block was copied from readRecord()
int shr; // Type of locking for the record
if ((openType_ == AS400File.READ_ONLY) ||
((openType_ == AS400File.READ_WRITE) && readNoUpdate_))
{ // Read only
shr = SHR_READ_NORM;
}
else
{ // READ_WRITE; get the record for update
shr = SHR_UPD_NORM;
}
// Get the records
// Initialize returned to be of TYPE_GET_FIRST
// As the loop continues, returned is of TYPE_GET_NEXT
for (Record[] returned = processReadReply(sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GET(dclName_, TYPE_GET_FIRST, shr, DATA_DTA_DTARCD), newCorrelationId()), false); //@B6C
returned != null;
returned = processReadReply(sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GET(dclName_, TYPE_GET_NEXT, shr, DATA_DTA_DTARCD), newCorrelationId()), false)) //@B6C
{
// The reply is an array of records, so add each of them to the vector
for (int i=0; i
0)
{
recArray = new Record[numRecs];
allRecords.copyInto(recArray);
}
close(); // Need to close the file since we opened it earlier.
} // The file is not supposed to be open to the user.
return recArray;
//@B0A: end block
//@B0D: start block
/*
// Connect to the server. Note: If we have already connected, that connection
// will be used.
connect();
// Send the request to read all records. Because the reply object may contain many records
// we need to deal with the possibility of chained replies.
Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestULDRECF(fileType, library_, file_, member_,
system_), server_.newCorrelationId());
Record[] records = null;
int index = lookForCodePoint(DDMTerm.RECORD, replys);
if (index == -1)
{ // Check for any RECAL terms. These contain the active record if we encounter an inactive record. This covers the
// case where records 1, 3, 5, 7, ... have been deleted. In this event we would have only RECAL's returned
// which would contain records 2, 4, 6, 8, ...
index = lookForCodePoint(DDMTerm.RECAL, replys);
}
if (index != -1)
{
// Records returned. Process the reply object(s)
// The last DDMTERM of the last reply received is a RECCNT and contains the number of records read.
int numReplies = replys.size();
DDMDataStream reply = (DDMDataStream)replys.elementAt(numReplies - 1);
int numRead = reply.get32bit(reply.data_.length - 4);
records = new Record[numRead];
if (numRead > 0)
{
int recordNumber = 1;
int recOffset;
int offset;
boolean done;
for (int i = index, j = 0; i < numReplies; ++i)
{
reply = (DDMDataStream)replys.elementAt(i);
done = false;
offset = 10;
while (!done)
{
// Find the next occurrence of a RECORD or RECAL term. We get a RECAL which will contain a
// RECORD when we encounter an inactive (deleted) record position. Otherwise we should just
// get a RECORD term
while (offset < reply.data_.length && ((reply.get16bit(offset - 2) != DDMTerm.RECORD) && (reply.get16bit(offset - 2) != DDMTerm.RECAL)))
{
offset += reply.get16bit(offset - 4);
}
if (offset >= reply.data_.length)
{
break;
}
// Determine offset of the record data and determine the record number
if (reply.get16bit(offset - 2) == DDMTerm.RECAL)
{ // We have encountered a spot for an inactive record. The RECAL term consists
// of the record number of the next active record along with the record data of the
// next active record.
recOffset = offset + 12; // Skip ahead to the record data
recordNumber = reply.get32bit(offset + 4); // Set record number to the record number of the
// active record
}
else
{ // This is an active record; we are already pointing to the record data and the record number
// was set appropriately last time through the loop.
recOffset = offset;
}
if (reply.get16bit(offset - 4) <= 0x7FFF)
{ // The record data immediately follows the code point if the
// record length + 4 is <= 32767
records[j] = recordFormat_.getNewRecord(reply.data_, recOffset);
}
else
{ // The record data starts 4 bytes after the code point if the
// record length + 4 is > 32767. The four bytes immediately
// after the code point contain the actual length of the record
// record data that follows.
records[j] = recordFormat_.getNewRecord(reply.data_, recOffset + 4);
}
// Set the record number of the record
try
{
records[j++].setRecordNumber(recordNumber++);
}
catch(PropertyVetoException e)
{ // We created the Record objects. There is no one to veto anything
} // so this is here to quit the compiler
if (j == numRead)
{
done = true;
}
// Get offset of next record
offset += reply.get16bit(offset - 4);
}
}
}
}
else if (replys.size() == 1 && ((DDMDataStream)replys.elementAt(0)).getCodePoint() == DDMTerm.RECCNT)
{
// If the file contains all deleted records, the only thing returned
// is a RECCNT. If this is the case, return an empty Record[]
return new Record[0];
}
else
{ // Error occurred
handleErrorReply(replys, 0);
}
return (records == null ? new Record[0] : records);
*/
//@B0D: end block
}
//@RBA
public Record[] readAllLong(String fileType, int bf)
throws AS400Exception,
AS400SecurityException,
InterruptedException,
IOException
{
//@B0A: Changed readAll() to not use the ULDRECF codepoint in the DDM data stream.
// This is because ULDRECF (Unload all records from file) does not handle
// null field values in records. (It throws us a data mapping error on
// the data stream, which would then cause an OutOfMemory exception
// or other strange errors in our code, because the data stream format
// was no longer correct.)
// readAll() now just uses the S38 extensions. It gets the first record
// and then gets the next record in a loop until null is returned.
// Using the S38 extensions gives better performance anyway (supposedly).
// See SequentialFile.readAll() and KeyedFile.readAll() for other changes.
// Other changes were also made in DDMRequestDataStream and DDMTerm.
//@B0A: start block
// readAll is supposed to return at least a Record[] of size 0, never null
Record[] recArray = new Record[0];
synchronized(this) // We synchronize because this file object
{ // isn't supposed to be open (as far as the user knows).
// Use a calculated blocking factor, else use a large blocking factor
//@D0M int bf = 2048/(recordFormat_.getNewRecord().getRecordLength() + 16); //@D0C
//@D0M if (bf <= 0) bf = 1; //@D0C
//@E0 - We don't want to use COMMIT_LOCK_LEVEL_ALL in case commitment control is on because
// inside readAll(), the file isn't supposed to be open, so we should treat it as such.
openFile2(AS400File.READ_ONLY, bf, AS400File.COMMIT_LOCK_LEVEL_NONE, fileType); //@D0C @E0C
// The vector to hold the records as we retrieve them.
Vector allRecords = new Vector();
// The following block was copied from readRecord()
int shr; // Type of locking for the record
if ((openType_ == AS400File.READ_ONLY) ||
((openType_ == AS400File.READ_WRITE) && readNoUpdate_))
{ // Read only
shr = SHR_READ_NORM;
}
else
{ // READ_WRITE; get the record for update
shr = SHR_UPD_NORM;
}
// Get the records
// Initialize returned to be of TYPE_GET_FIRST
// As the loop continues, returned is of TYPE_GET_NEXT
for (Record[] returned = processReadReplyLong(sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GET(dclName_, TYPE_GET_FIRST, shr, DATA_DTA_DTARCD), newCorrelationId()), false); //@B6C
returned != null;
returned = processReadReplyLong(sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GET(dclName_, TYPE_GET_NEXT, shr, DATA_DTA_DTARCD), newCorrelationId()), false)) //@B6C
{
// The reply is an array of records, so add each of them to the vector
for (int i=0; i 0)
{
recArray = new Record[numRecs];
allRecords.copyInto(recArray);
}
close(); // Need to close the file since we opened it earlier.
} // The file is not supposed to be open to the user.
return recArray;
}
/**
*Reads the first record with the specified key based on the specified type of read.
*@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 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.
**/
public Record read(Object[] key, int searchType)
throws AS400Exception,
AS400SecurityException,
InterruptedException,
IOException
{
int shr; // Type of locking for the record
if ((openType_ == AS400File.READ_ONLY) ||
((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) // @A1A
{
// Read only
shr = SHR_READ_NORM;
}
else
{ // READ_WRITE
shr = SHR_UPD_NORM;
}
// Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GETK(dclName_, recordFormat_, type, shr, DATA_DTA_DTARCD, key, system_), server_.newCorrelationId()); // @A1D
Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GETK(dclName_, recordFormat_, recordFormatCTLLName_, searchType, shr, DATA_DTA_DTARCD, key, system_), newCorrelationId()); // @A1A @B6C
// Call processReadReply to extract the records read (or throw an
// exception if appropriate)
Record[] returned = processReadReply(replys, false); // @A1C
if (cacheRecords_) //@C0A
{
//@C0A
cache_.setIsEmpty(); //@C0A
} //@C0A
return(returned == null)? null : returned[0];
}
//@RBA
public Record readLong(Object[] key, int searchType)
throws AS400Exception,
AS400SecurityException,
InterruptedException,
IOException
{
int shr; // Type of locking for the record
if ((openType_ == AS400File.READ_ONLY) ||
((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) // @A1A
{
// Read only
shr = SHR_READ_NORM;
}
else
{ // READ_WRITE
shr = SHR_UPD_NORM;
}
// Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GETK(dclName_, recordFormat_, type, shr, DATA_DTA_DTARCD, key, system_), server_.newCorrelationId()); // @A1D
Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GETK(dclName_, recordFormat_, recordFormatCTLLName_, searchType, shr, DATA_DTA_DTARCD, key, system_), newCorrelationId()); // @A1A @B6C
// Call processReadReply to extract the records read (or throw an
// exception if appropriate)
Record[] returned = processReadReplyLong(replys, false); // @A1C
if (cacheRecords_) //@C0A
{
//@C0A
cache_.setIsEmpty(); //@C0A
} //@C0A
return(returned == null)? null : returned[0];
}
// @A1A
/**
*Reads the first record with the specified key based on the specified type of read.
*@param key The byte array that contains the byte 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.
*@param numberOfKeyFields The number of key fields contained in the byte array key.
*@return The record read.
*@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.
**/
public Record read(byte[] key, int searchType, int numberOfKeyFields)
throws AS400Exception,
AS400SecurityException,
InterruptedException,
IOException
{
int shr; // Type of locking for the record
if ((openType_ == AS400File.READ_ONLY) ||
((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) // @A1A
{
// Read only
shr = SHR_READ_NORM;
}
else
{ // READ_WRITE
shr = SHR_UPD_NORM;
}
// Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GETK(dclName_, recordFormat_, type, shr, DATA_DTA_DTARCD, key, system_, numberOfKeyFields), server_.newCorrelationId()); // @A1D
Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GETK(dclName_, recordFormatCTLLName_, searchType, shr, DATA_DTA_DTARCD, key, system_, numberOfKeyFields), newCorrelationId()); // @A1A @B6C
// Call processReadReply to extract the records read (or throw an
// exception if appropriate)
Record[] returned = processReadReply(replys, false); // @A1C
if (cacheRecords_) //@C0A
{
//@C0A
cache_.setIsEmpty(); //@C0A
} //@C0A
return(returned == null)? null : returned[0];
}
//@RBA
public Record readLong(byte[] key, int searchType, int numberOfKeyFields)
throws AS400Exception,
AS400SecurityException,
InterruptedException,
IOException
{
int shr; // Type of locking for the record
if ((openType_ == AS400File.READ_ONLY) ||
((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) // @A1A
{
// Read only
shr = SHR_READ_NORM;
}
else
{ // READ_WRITE
shr = SHR_UPD_NORM;
}
// Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GETK(dclName_, recordFormat_, type, shr, DATA_DTA_DTARCD, key, system_, numberOfKeyFields), server_.newCorrelationId()); // @A1D
Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GETK(dclName_, recordFormatCTLLName_, searchType, shr, DATA_DTA_DTARCD, key, system_, numberOfKeyFields), newCorrelationId()); // @A1A @B6C
// Call processReadReply to extract the records read (or throw an
// exception if appropriate)
Record[] returned = processReadReplyLong(replys, false); // @A1C
if (cacheRecords_) //@C0A
{
//@C0A
cache_.setIsEmpty(); //@C0A
} //@C0A
return(returned == null)? null : returned[0];
}
/**
*Reads a record from the file. Which record to read is determined by the type
*argument.
*@param searchType The type of get to execute. Valid values are:
*
*- TYPE_GET_FIRST
*
- TYPE_GET_NEXT
*
- TYPE_GET_LAST
*
- TYPE_GET_PREV
*@return the record read
*@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.
**/
public Record readRecord(int searchType)
throws AS400Exception,
AS400SecurityException,
InterruptedException,
IOException
{
int shr; // Type of locking for the record
// @A1C
if ((openType_ == AS400File.READ_ONLY) ||
((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) // @A1A
{
// Read only
shr = SHR_READ_NORM;
}
else
{ // READ_WRITE; get the record for update
shr = SHR_UPD_NORM;
}
// Send the get S38GET request
Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GET(dclName_, searchType, shr, DATA_DTA_DTARCD), newCorrelationId()); //@B6C
// Call processReadReply to extract the records read (or throw an
// exception if appropriate)
Record[] returned = processReadReply(replys, false); // @A1C
return(returned == null)? null : returned[0];
}
//@RBA
public Record readRecordLong(int searchType)
throws AS400Exception,
AS400SecurityException,
InterruptedException,
IOException
{
int shr; // Type of locking for the record
// @A1C
if ((openType_ == AS400File.READ_ONLY) ||
((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) // @A1A
{
// Read only
shr = SHR_READ_NORM;
}
else
{ // READ_WRITE; get the record for update
shr = SHR_UPD_NORM;
}
// Send the get S38GET request
Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GET(dclName_, searchType, shr, DATA_DTA_DTARCD), newCorrelationId()); //@B6C
// Call processReadReply to extract the records read (or throw an
// exception if appropriate)
Record[] returned = processReadReplyLong(replys, false); // @A1C
return(returned == null)? null : returned[0];
}
/**
*Reads records from the file. The next or previous 'blockingFactor_'
*records are retrieved depending on the direction specified.
*@param direction
*@return the records read
*@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.
**/
public Record[] readRecords(int direction)
throws AS400Exception,
AS400SecurityException,
InterruptedException,
IOException
{
int searchType = (direction == DDMRecordCache.FORWARD ? TYPE_GET_NEXT :
TYPE_GET_PREV);
// Send the S38GETM request
Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GETM(dclName_, blockingFactor_, searchType, SHR_READ_NORM, DATA_DTA_DTARCD, 0x01), newCorrelationId()); //@B6C
// Call processReadReply to extract the records read (or throw an
// exception if appropriate)
return processReadReply(replys, false); // @A1C
}
//@RBA
public Record[] readRecordsLong(int direction)
throws AS400Exception,
AS400SecurityException,
InterruptedException,
IOException
{
int searchType = (direction == DDMRecordCache.FORWARD ? TYPE_GET_NEXT :
TYPE_GET_PREV);
// Send the S38GETM request
Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestS38GETM(dclName_, blockingFactor_, searchType, SHR_READ_NORM, DATA_DTA_DTARCD, 0x01), newCorrelationId()); //@B6C
// Call processReadReply to extract the records read (or throw an
// exception if appropriate)
return processReadReplyLong(replys, false); // @A1C
}
/**
*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 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.
*@exception UnknownHostException If the server cannot be located.
**/
public void rollback() throws AS400Exception, AS400SecurityException, InterruptedException, IOException
{
// Connect to the server. Note: If we have already connected, that connection
// will be used.
connect();
// Send the rollback data stream request
Vector replys = sendRequestAndReceiveReplies(DDMRequestDataStream.getRequestRLLBCKUOW(), newCorrelationId()); //@B6C
// Reply expected: ENDUOWRM, with UOWDSP parameter = 1
if (replys.size() == 1 && ((DDMDataStream)replys.elementAt(0)).getCodePoint() == DDMTerm.ENDUOWRM)
{
DDMEndUnitOfWorkReply uowReply = new DDMEndUnitOfWorkReply(((DDMDataStream)replys.elementAt(0)).data_);
if (uowReply.getStatus() != 0x02)
{
// Status of logical unit of work committed not returned; should not happen unless
// we are constructing the request wrong.
Trace.log(Trace.ERROR, "AS400FileImplRemote.rollback()",
uowReply.data_);
throw new InternalErrorException(InternalErrorException.UNKNOWN, uowReply.getStatus());
}
}
else
{
handleErrorReply(replys, 0);
}
}
/**
*Sends a request and receives all the replies. This method is used when the potential
*for chained replies exists.
*@param req The request to be sent.
*@param correlationId The correlation id for the request.
*@exception AS400Exception If the server returns an error message.
*@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.
**/
private Vector sendRequestAndReceiveReplies(DDMDataStream req, int correlationId)
throws InterruptedException, IOException, AS400SecurityException
{
connect();
DDMDataStream reply = null;
try
{
synchronized(server_) //@F1A - make sure all of our operations are atomic
{ //@F1A - we are synchronized so we don't interrupt any other sends
// that are sensitive.
server_.send(req, correlationId);
reply = (DDMDataStream)server_.receive(correlationId);
} //@F1A
}
catch (ConnectionDroppedException e)
{
// Connection dropped. Disconnect server and rethrow.
Trace.log(Trace.ERROR, "ConnectionDroppedException:", e);
system_.disconnectServer(server_);
//@C1 - Setting the server_ object to null means that
// any operations on this AS400File object after the connection has been
// dropped will result in a NullPointerException. By leaving the server_ object
// around, any subsequent operations should also throw a ConnectionDroppedException.
//@C1D server_ = null;
resetState();
throw e;
}
// Receive all replies from the read into a vector.
Vector replys = new Vector();
while (reply.isChained())
{
replys.addElement(reply);
try
{
reply = (DDMDataStream)server_.receive(correlationId);
}
catch (ConnectionDroppedException e)
{
// Connection dropped. Disconnect server and rethrow.
Trace.log(Trace.ERROR, "ConnectionDroppedException:", e);
system_.disconnectServer(server_);
//@C1 - Setting the server_ object to null means that
// any operations on this AS400File object after the connection has been
// dropped will result in a NullPointerException. By leaving the server_ object
// around, any subsequent operations should also throw a ConnectionDroppedException.
//@C1D server_ = null;
resetState();
throw e;
}
}
// Add the unchained reply to the vector of replys
replys.addElement(reply);
return replys;
}
/**
*Sets the declared file name (DCLNAM). The declared file name for each
*file object must be unique. This method will generate a unique declared file
*name.
**/
public void setDCLName()
{
// Convert nextDCLName_ to a Long and then to a string
long nextDCLName;
synchronized (nextDCLNameLock_) {
nextDCLName = nextDCLName_++; // need to synchronize the incrementation
}
String nextDCLNameAsString = Long.toString(nextDCLName);
// Copy EBCDIC version of nextDCLName to dclNAme_ and blank pad dclName_
// to 8 bytes
for (int i = 0; i < nextDCLNameAsString.length(); i++)
{
char c = nextDCLNameAsString.charAt(i);
dclName_[i] = (byte) (c & 0x000f | 0x00f0);
}
padBytes(dclName_, nextDCLNameAsString.length(),
8 - nextDCLNameAsString.length(), (byte)0x40);
}
/**
*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 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.
**/
public void update(Record record)
throws AS400Exception,
AS400SecurityException,
InterruptedException,
IOException
{
if (record.getRecordLength() != openFeedback_.getRecordLength())
{
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);
}
connect();
// getObjectS38BUF requires an array of records
Record[] records = new Record[1];
records[0] = record;
// We will be chaining an S38BUF to the S38UPDAT request. This requires that
// the correlation ids be the same.
int correlationId = newCorrelationId(); //@B6C
// Send the S38UPDAT request followed by the S38BUF object containing the
// record with which to update.
DDMRequestDataStream req = DDMRequestDataStream.getRequestS38UPDAT( TYPE_GET_SAME, SHR_UPD_NORM, DATA_DTA_DTARCD, dclName_);
req.setIsChained(true); // Indicate we are chaining an object to the request
req.setHasSameRequestCorrelation(true); // Indicate that they have the same
// correlation ids
// Start of changes for ----------------------------------------- @A2A
// 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
{
int numberOfRecordFormats = rfCache_.length;
for(int i = 0; i < numberOfRecordFormats; ++i)
{
maxNumberOfFieldsPerFormatInFile =
Math.max(maxNumberOfFieldsPerFormatInFile, rfCache_[i].getNumberOfFields());
}
}
// End of changes for ------------------------------------------- @A2A
// Get the S38BUF object(s) to send after the request
// Because we are updating, there will only be one item in dataToSend
DDMObjectDataStream[] dataToSend =
DDMObjectDataStream.getObjectS38BUF(records, openFeedback_, ssp_, // #SSPDDM1 - add ssp_ parm
maxNumberOfFieldsPerFormatInFile); //@A2C
Vector replys = null; //@F1A
synchronized(server_) //@F1A - both datastreams must be written atomically
{ //@F1A
try
{
server_.send(req, correlationId);
replys = sendRequestAndReceiveReplies(dataToSend[0], correlationId); //@F1M
}
catch (ConnectionDroppedException e)
{
// Connection dropped. Disconnect server and rethrow.
Trace.log(Trace.ERROR, "ConnectionDroppedException:", e);
system_.disconnectServer(server_);
//@C1 - Setting the server_ object to null means that
// any operations on this AS400File object after the connection has been
// dropped will result in a NullPointerException. By leaving the server_ object
// around, any subsequent operations should also throw a ConnectionDroppedException.
//@C1D server_ = null;
resetState();
throw e;
}
} //@F1A
// Reply expected: S38IOFB
if (((DDMDataStream)replys.elementAt(0)).getCodePoint() == DDMTerm.S38IOFB)
{
if (replys.size() != 1)
{
handleErrorReply(replys, 1);
}
}
else
{ // Error occurred
handleErrorReply(replys, 0);
}
}
/**
*Verifies the reply datastream as an S38MSGRM with the specified
*information. If msgID is not null, svrCode is not checked.
*If msgId is null, svrCode is verified.
@param replyParm The DDM reply data stream.
@param msgId The message id that this reply should contain.
@param svrCode The severity code that this reply should contain.
* @return true if datais is a S38MSGRM with specified information
*@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.
**/
public boolean verifyS38MSGRM(DDMDataStream replyParm, String msgId, int svrCode)
throws AS400Exception,
AS400SecurityException,
InterruptedException,
IOException
{
if (replyParm instanceof DDMReplyDataStream)
{
DDMReplyDataStream reply = (DDMReplyDataStream) replyParm;
DDMAS400MessageReply msgReply = new DDMAS400MessageReply(system_, reply.data_);
AS400Message[] msgs = msgReply.getAS400MessageList();
for (int i = 0; i < msgs.length; ++i)
{
if (msgId != null)
{ // Verify based on msgid only
if (msgs[i].getID().equalsIgnoreCase(msgId))
{
return true;
}
}
else
{ // Verify based on severity code only
if (msgs[i].getSeverity() == svrCode)
{
return true;
}
}
}
}
return false;
}
/**
*Writes an array of records to the file.
*@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 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.
**/
public void write(Record[] records)
throws AS400Exception,
AS400SecurityException,
InterruptedException,
IOException
{
if (records[0].getRecordLength() != openFeedback_.getRecordLength())
{
// Note: The typical cause of this error is that the application neglected to call AS400File.setRecordFormat() prior to calling open() or readAll().
if (Trace.traceOn_)
{
Trace.log(Trace.ERROR, "Incorrect record length for file: " +
"Expected " + String.valueOf(openFeedback_.getRecordLength()) +
", got " + String.valueOf(records[0].getRecordLength()));
}
throw new ExtendedIllegalArgumentException("records", ExtendedIllegalArgumentException. PARAMETER_VALUE_NOT_VALID);
}
connect();
// We will be chaining the S38BUF to the request, so the correlation ids must match
int correlationId = newCorrelationId(); //@B6C
DDMRequestDataStream req = DDMRequestDataStream.getRequestS38PUTM(dclName_);
req.setIsChained(true); // Indicate that the request is chained
req.setHasSameRequestCorrelation(true); // Indicate hat the ids will match
// Start of changes for ----------------------------------------- @A2A
// 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
{
int numberOfRecordFormats = rfCache_.length;
for(int i = 0; i < numberOfRecordFormats; ++i)
{
maxNumberOfFieldsPerFormatInFile =
Math.max(maxNumberOfFieldsPerFormatInFile, rfCache_[i].getNumberOfFields());
}
}
// End of changes for ------------------------------------------- @A2A
// Get the S38BUF object(s) containing the records to write.
DDMObjectDataStream[] dataToSend =
DDMObjectDataStream.getObjectS38BUF(records, openFeedback_, ssp_, // #SSPDDM1 - add ssp_ parm
maxNumberOfFieldsPerFormatInFile); //@A2A
// It is possible that we will have more than one S38BUF to send. This case
// occurs when the blocking factor is less than the number records to be written.
// In that case we do multiple of S38PUTMs of blocking factor number of records.
Vector replys;
synchronized(server_) //@F1A - both datastreams must be written atomically
{ //@F1A
for (int i = 0; i < dataToSend.length; ++i)
{ // For each S38BUF object, send the S38PUTM followed by the S38BUF
server_.send(req, correlationId);
replys = sendRequestAndReceiveReplies(dataToSend[i], correlationId);
// Reply expected: S38IOFB
if (((DDMDataStream)replys.elementAt(0)).getCodePoint() == DDMTerm.S38IOFB)
{
if (replys.size() != 1)
{
handleErrorReply(replys, 1);
}
}
else
{ // Error occurred
handleErrorReply(replys, 0);
}
}
} //@F1A
}
// @B1A
// @B6C
private int newCorrelationId()
{
synchronized(correlationIdLock_)
{
if (lastCorrelationId_ == 0x7fff) lastCorrelationId_ = 0;
return ++lastCorrelationId_;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy