src.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-jdk9 Show documentation
Show all versions of jt400-jdk9 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.ObjectInputStream;
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