jtopenlite.com.ibm.jtopenlite.ddm.DDMConnection 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
///////////////////////////////////////////////////////////////////////////////
//
// JTOpenLite
//
// Filename: DDMConnection.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) 2011-2012 International Business Machines Corporation and
// others. All rights reserved.
//
///////////////////////////////////////////////////////////////////////////////
package com.ibm.jtopenlite.ddm;
import com.ibm.jtopenlite.*;
import java.io.*;
import java.net.*;
import java.util.*;
import javax.net.ssl.SSLSocketFactory;
/**
* Represents a TCP/IP socket connection to the System i Distributed Data Management (DDM) host server (QUSRWRK/QRWTSRVR job).
*
* See {@link com.ibm.jtopenlite.samples.DDMRead} for an example of using DDM.
**/
public class DDMConnection extends HostServerConnection
{
private static final boolean DEBUG = false;
public static final int DEFAULT_DDM_SERVER_PORT = 446;
public static final int DEFAULT_SSL_DDM_SERVER_PORT = 446;
private final byte[] messageBuf_ = new byte[1024];
private final char[] charBuffer_ = new char[1024];
private int dclNamCounter_ = 0;
private int correlationID_ = 1;
private int newCorrelationID()
{
if (correlationID_ == 0x7FFF) correlationID_ = 0;
return ++correlationID_;
}
private DDMConnection(SystemInfo info, Socket socket, HostInputStream in, HostOutputStream out, String user, String jobString)
{
super(info, user, jobString, socket, in, out);
}
/**
* No-op. The DDM host server does not use an end job datastream.
**/
protected void sendEndJobRequest() throws IOException
{
}
/**
* Establishes a new socket connection to the specified system and authenticates the specified user and password.
**/
public static DDMConnection getConnection(String system, String user, String password) throws IOException
{
return getConnection(false, system, user, password);
}
/**
* Establishes a new socket connection to the specified system and authenticates the specified user and password.
**/
public static DDMConnection getConnection(final boolean isSSL, String system, String user, String password) throws IOException
{
SignonConnection conn = SignonConnection.getConnection(isSSL, system, user, password);
try
{
return getConnection(isSSL, conn.getInfo(), user, password);
}
finally
{
conn.close();
}
}
/**
* Establishes a new socket connection to the specified system and authenticates the specified user and password.
**/
public static DDMConnection getConnection(SystemInfo info, String user, String password) throws IOException
{
return getConnection(false, info, user, password);
}
/**
* Establishes a new socket connection to the specified system and authenticates the specified user and password.
**/
public static DDMConnection getConnection(final boolean isSSL, SystemInfo info, String user, String password) throws IOException
{
return getConnection(isSSL, info, user, password, isSSL ? DEFAULT_SSL_DDM_SERVER_PORT: DEFAULT_DDM_SERVER_PORT);
}
/**
* Establishes a new socket connection to the specified system and port, and authenticates the specified user and password.
**/
public static DDMConnection getConnection(SystemInfo info, String user, String password, int ddmPort) throws IOException
{
return getConnection(false, info, user, password, ddmPort);
}
/**
* Establishes a new socket connection to the specified system and port, and authenticates the specified user and password.
**/
public static DDMConnection getConnection(final boolean isSSL, SystemInfo info, String user, String password, int ddmPort) throws IOException
{
if (ddmPort < 0 || ddmPort > 65535)
{
throw new IOException("Bad DDM port: "+ddmPort);
}
DDMConnection conn = null;
Socket ddmServer = isSSL? SSLSocketFactory.getDefault().createSocket(info.getSystem(), ddmPort) : new Socket(info.getSystem(), ddmPort);
InputStream in = ddmServer.getInputStream();
OutputStream out = ddmServer.getOutputStream();
try
{
// Exchange random seeds.
HostOutputStream dout = new HostOutputStream(new BufferedOutputStream(out, 1024));
sendEXCSATRequest(dout);
dout.flush();
HostInputStream din = new HostInputStream(new BufferedInputStream(in, 32768));
int length = din.readShort();
if (length < 10)
{
throw DataStreamException.badLength("ddmExchangeServerAttributes", length);
}
int gdsID = din.read();
int typeCorrelationChainedContinue = din.read(); // bit mask
int correlation = din.readShort();
int excSATLength = din.readShort(); // LL.
int codepoint = din.readShort(); // CP.
if (codepoint != 0x1443) // EXCSATRD
{
throw DataStreamException.badReply("ddmExchangeServerAttributes", codepoint);
}
int numRead = 10;
byte[] extNam = null; // Job string.
while (length-numRead > 4 && extNam == null)
{
int extNamLL = din.readShort();
int extNamCP = din.readShort();
numRead += 4;
if (extNamCP == 0x115E) // EXTNAM
{
extNam = new byte[extNamLL-4];
din.readFully(extNam);
numRead += extNam.length;
}
else
{
din.skipBytes(extNamLL-4);
numRead += (extNamLL-4);
}
}
din.skipBytes(length-numRead);
din.end();
final String jobString = extNam != null ? Conv.ebcdicByteArrayToString(extNam, 0, extNam.length) : null;
long seed = sendACCSECRequest(dout, info.getPasswordLevel() >= 2);
byte[] clientSeed = Conv.longToByteArray(seed);
dout.flush();
length = din.readShort();
if (length < 28)
{
throw DataStreamException.badLength("ddmAccessMethodExchange", length);
}
gdsID = din.read();
typeCorrelationChainedContinue = din.read();
correlation = din.readShort();
//length = din.readShort(); // LL.
din.skipBytes(2);
codepoint = din.readShort(); // CP.
if (codepoint != 0x14AC) // ACCSECRD
{
throw DataStreamException.badReply("ddmAccessMethodExchange", codepoint);
}
din.skipBytes(10);
byte[] serverSeed = new byte[8];
din.readFully(serverSeed);
din.skipBytes(length-28);
din.end();
byte[] userBytes = getUserBytes(user, info.getPasswordLevel());
byte[] passwordBytes = getPasswordBytes(password, info.getPasswordLevel());
password = null;
byte[] encryptedPassword = getEncryptedPassword(userBytes, passwordBytes, clientSeed, serverSeed, info.getPasswordLevel());
byte[] userEBCDICBytes = (info.getPasswordLevel() < 2) ? userBytes : getUserBytes(user, 0);
sendSECCHKRequest(dout, userEBCDICBytes, encryptedPassword);
dout.flush();
length = din.readShort();
if (length < 21)
{
throw DataStreamException.badLength("ddmSecurityCheck", length);
}
gdsID = din.read();
typeCorrelationChainedContinue = din.read();
correlation = din.readShort();
//length = din.readShort(); // LL.
din.skipBytes(2);
codepoint = din.readShort(); // CP.
if (codepoint != 0x1219) // SECCHKRD
{
throw DataStreamException.badReply("ddmSecurityCheckSECCHKRD", codepoint);
}
din.skipBytes(8);
codepoint = din.readShort();
if (codepoint != 0x11A4) // SECCHKCD.
{
throw DataStreamException.badReply("ddmSecurityCheckSECCHKCD", codepoint);
}
int rc = din.read();
if (rc != 0)
{
throw DataStreamException.badReturnCode("ddmSecurityCheck", rc);
}
din.skipBytes(length-21);
din.end();
conn = new DDMConnection(info, ddmServer, din, dout, user, jobString);
return conn;
}
finally
{
if (conn == null)
{
in.close();
out.close();
ddmServer.close();
}
}
}
// Copied from HostServerConnection.
static byte[] getUserBytes(String user, int level) throws IOException
{
if (level < 2)
{
if (user.length() > 10)
{
throw new IOException("User too long");
}
byte[] user37 = Conv.blankPadEBCDIC10(user.toUpperCase());
return user37;
}
else
{
byte[] b = new byte[20];
Conv.stringToBlankPadUnicodeByteArray(user.toUpperCase(), b, 0, 20);
return b;
}
}
// Copied from HostServerConnection.
static byte[] getPasswordBytes(String password, int level) throws IOException
{
if (level < 2)
{
// Prepend a Q to numeric password.
if (password.length() > 0 && Character.isDigit(password.charAt(0)))
{
password = "Q"+password;
}
if (password.length() > 10)
{
throw new IOException("Password too long");
}
byte[] password37 = Conv.blankPadEBCDIC10(password.toUpperCase());
return password37;
}
else
{
return Conv.stringToUnicodeByteArray(password);
}
}
/**
* Closes the specified file and returns any messages that were issued.
**/
public Message[] close(DDMFile file) throws IOException {
List messageList = closeReturnMessageList(file);
Message[] outMessages = new Message[messageList.size()];
for (int i = 0; i < messageList.size(); i++) {
outMessages[i] = messageList.get(i);
}
return outMessages;
}
public List closeReturnMessageList(DDMFile file) throws IOException
{
byte[] dclNam = file.getDCLNAM();
sendS38CloseRequest(out_, dclNam);
out_.flush();
int length = in_.readShort();
if (length < 10)
{
throw DataStreamException.badLength("ddmS38Close", length);
}
int gdsID = in_.read();
int typeCorrelationChainedContinue = in_.read(); // bit mask
int correlation = in_.readShort();
int numRead = 8;
ArrayList messages = new ArrayList();
int[] msgNumRead = new int[1];
while (numRead+4 < length)
{
int ll = in_.readShort();
int cp = in_.readShort();
numRead += 4;
if (cp == 0xD201) // S38MSGRM
{
Message msg = getMessage(in_, ll, msgNumRead);
numRead += msgNumRead[0];
if (msg != null)
{
messages.add(msg);
}
}
else
{
in_.skipBytes(ll-4);
numRead += ll-4;
}
}
in_.skipBytes(length-numRead);
in_.end();
return messages;
}
private Message getMessage(HostInputStream din, final int ll, int[] saved) throws IOException
{
int severity = -1;
String messageID = null;
String messageText = null;
byte[] b = null;
char[] c = null;
int msgNumRead = 0;
while (msgNumRead < ll-4)
{
int msgLength = din.readShort();
int msgCodepoint = din.readShort();
switch (msgCodepoint)
{
case 0x1149: // SVRCOD
severity = din.readShort();
din.skipBytes(msgLength-6);
break;
case 0xD112: // S38MID
final boolean tooLong4 = msgLength > 1028;
final int msgLength4 = msgLength-4;
b = tooLong4 ? new byte[msgLength4] : messageBuf_;
c = tooLong4 ? new char[msgLength4] : charBuffer_;
din.readFully(b, 0, msgLength4);
messageID = Conv.ebcdicByteArrayToString(b, 0, msgLength4, c);
break;
case 0xD116: // S38MTEXT
final boolean tooLong6 = msgLength > 1030;
final int msgLength6 = msgLength-6;
b = tooLong6 ? new byte[msgLength6] : messageBuf_;
c = tooLong6 ? new char[msgLength6] : charBuffer_;
din.skipBytes(2);
din.readFully(b, 0, msgLength6);
messageText = Conv.ebcdicByteArrayToString(b, 0, msgLength6, c);
break;
default:
din.skipBytes(msgLength-4);
break;
}
msgNumRead += msgLength;
}
saved[0] = msgNumRead;
return messageID == null ? null : new Message(messageID, messageText, severity);
}
/**
* Opens the specified file for sequential read-only access and a preferred batch size of 100.
* @param library The name of the library in which the file resides. For example, "QSYS".
* @param file The name of the physical or logical file.
* @param member The member within the file. This can be a special value such as "*FIRST".
* @param recordFormat The name of the record format. This value can also be obtained from {@link DDMRecordFormat#getName DDMRecordFormat.getName()}.
**/
public DDMFile open(String library, String file, String member, String recordFormat) throws IOException
{
return open(library, file, member, recordFormat, DDMFile.READ_ONLY, false, 100, 1);
}
/**
* Opens the specified file for reading or writing records.
* @param library The name of the library in which the file resides. For example, "QSYS".
* @param file The name of the physical or logical file.
* @param member The member within the file. This can be a special value such as "*FIRST".
* @param recordFormat The name of the record format. This value can also be obtained from {@link DDMRecordFormat#getName DDMRecordFormat.getName()}.
* @param readWriteType The read-write access type to use. Allowed values are:
*
* - {@link DDMFile#READ_ONLY DDMFile.READ_ONLY}
* - {@link DDMFile#READ_WRITE DDMFile.READ_WRITE}
* - {@link DDMFile#WRITE_ONLY DDMFile.WRITE_ONLY}
*
* @param keyed Indicates if the file should be opened for sequential or keyed access.
* @param preferredBatchSize The number of records to read or write at a time. This is a preferred number because the DDMConnection
* may decide to use a different batch size depending on circumstances (e.g. READ_WRITE files always use a batch size of 1).
* @param numBuffers The number of data buffers to allocate for use when reading new records. The DDMConnection will round-robin
* through the data buffers as it calls {@link DDMReadCallback#newRecord DDMReadCallback.newRecord()}. This can be useful
* for multi-threaded readers that process new record data, such as {@link DDMThreadedReader DDMThreadedReader}. In such cases,
* each processing thread could be assigned a specific data buffer, to avoid contention. See {@link DDMDataBuffer DDMDataBuffer}.
**/
public DDMFile open(String library, String file, String member, String recordFormat, int readWriteType, boolean keyed, int preferredBatchSize, int numBuffers) throws IOException
{
final boolean doWrite = readWriteType == DDMFile.READ_WRITE || readWriteType == DDMFile.WRITE_ONLY;
final boolean doRead = readWriteType == DDMFile.READ_WRITE || readWriteType == DDMFile.READ_ONLY || !doWrite;
if ((doWrite && doRead) || preferredBatchSize <= 0) preferredBatchSize = 1;
preferredBatchSize = preferredBatchSize & 0x7FFF;
final byte[] dclNam = generateDCLNAM();
sendS38OpenRequest(out_, file, library, member, doRead, doWrite, keyed, recordFormat, dclNam, preferredBatchSize);
out_.flush();
int length = in_.readShort();
if (length < 10)
{
throw DataStreamException.badLength("ddmS38Open", length);
}
int gdsID = in_.read();
int type = in_.read(); // bit mask
boolean isChained = (type & 0x40) != 0;
int correlation = in_.readShort();
int ll = in_.readShort();
int cp = in_.readShort();
int numRead = 10;
if (DEBUG) System.out.println("open: "+gdsID+","+type+","+isChained+","+correlation+","+ll+","+cp);
while (cp != 0xD404 && numRead+4 <= length)
{
if (cp == 0xD201) // S38MSGRM
{
int[] numMsgRead = new int[1];
Message msg = getMessage(in_, ll, numMsgRead);
numRead += numMsgRead[0];
if (msg != null)
{
throw DataStreamException.errorMessage("ddmS38Open", msg);
}
}
else
{
if (DEBUG) System.out.println("Got cp "+Integer.toHexString(cp));
in_.skipBytes(ll-4);
numRead += (ll-4);
}
ll = in_.readShort();
cp = in_.readShort();
numRead += 4;
}
if (cp != 0xD404) // S38OPNFB
{
throw DataStreamException.badReply("ddmS38Open", cp);
}
int openType = in_.readByte();
final byte[] b = new byte[10];
final char[] c = new char[10];
in_.readFully(b);
String realFile = Conv.ebcdicByteArrayToString(b, 0, b.length, c);
in_.readFully(b);
String realLibrary = Conv.ebcdicByteArrayToString(b, 0, b.length, c);
in_.readFully(b);
String realMember = Conv.ebcdicByteArrayToString(b, 0, b.length, c);
int recordLength = in_.readShort();
in_.skipBytes(10);
int numRecordsReturned = in_.readInt();
in_.readFully(b, 0, 2);
String accessType = Conv.ebcdicByteArrayToString(b, 0, 2, c);
boolean supportDuplicateKeys = in_.readByte() == 0x00C4;
boolean isSourceFile = in_.readByte() == 0x00E8;
in_.skipBytes(10); // UFCB parameters
int maxBlockedRecordsTransferred = in_.readShort();
int recordIncrement = in_.readShort();
int openFlags1 = in_.readByte();
in_.skipBytes(6); // Number of associated physical file members, and other stuff?
int maxRecordLength = in_.readShort();
int recordWaitTime = in_.readInt();
int openFlags2 = in_.readShort();
int nullFieldByteMapOffset = in_.readShort();
int nullKeyFieldByteMapOffset = in_.readShort();
in_.skipBytes(4); // Other stuff?
int ccsid = in_.readShort();
int totalFixedFieldLength = in_.readShort();
numRead += 92;
in_.skipBytes(length-numRead); // Min record length and other stuff?
while (isChained)
{
// Skip the rest.
length = in_.readShort();
if (length < 10)
{
throw DataStreamException.badLength("ddmS38Open", length);
}
gdsID = in_.read();
type = in_.read(); // bit mask
isChained = (type & 0x40) != 0;
correlation = in_.readShort();
numRead = 6;
in_.skipBytes(length-6);
}
in_.end();
byte[] recordFormatName = Conv.blankPadEBCDIC10(recordFormat);
return new DDMFile(realLibrary, realFile, realMember, recordFormatName, dclNam, readWriteType, recordLength, recordIncrement, preferredBatchSize, nullFieldByteMapOffset, numBuffers);
}
/**
* Retrieves the member descriptions for the specified file.
**/
public List getFileMemberDescriptions(final String library, final String file) throws IOException
{
DDMFile f = new DDMFile(library, file, null, null, null, DDMFile.READ_ONLY, 0, 0, 0, 0, 1);
return getFileMemberDescriptions(f);
}
/**
* Retrieves the member descriptions for the specified file.
**/
public List getFileMemberDescriptions(final DDMFile file) throws IOException
{
List messages = executeReturnMessageList("DSPFD FILE("+file.getLibrary().trim()+"/"+file.getFile().trim()+") TYPE(*MBR) OUTPUT(*OUTFILE) OUTFILE(QTEMP/TB2FD)");
if (messages.size() == 0)
{
throw new IOException("DSPFFD failed to return success message");
}
boolean error = false;
for (int i=0; !error && i messages = executeReturnMessageList("DSPFFD FILE("+file.getLibrary().trim()+"/"+file.getFile().trim()+") OUTPUT(*OUTFILE) OUTFILE(QTEMP/TB2FFD)");
if (messages.size() == 0)
{
throw new IOException("DSPFFD failed to return success message");
}
boolean error = false;
for (int i=0; !error && i blockingFactor ? blockingFactor : numRecords;
int id = newCorrelationID();
while (startingRecordNumber < numRecords)
{
if (startingRecordNumber+batchSize >= numRecords) batchSize = numRecords-startingRecordNumber;
sendS38PUTMRequest(out_, file.getDCLNAM(), id);
sendS38BUFRequest(file, out_, id, file.getRecordIncrement(), listener, file.getRecordLength(), startingRecordNumber, batchSize);
out_.flush();
handleReply(file, "ddmS38PUTM", null);
startingRecordNumber += batchSize;
}
}
/**
* Updates the current record with the specified data.
**/
public void updateCurrent(DDMFile file, byte[] data, int offset, boolean[] nullFieldValues) throws IOException
{
final int id = sendS38UPDATRequest(out_, file.getDCLNAM());
file.getEventBuffer().setEventType(DDMCallbackEvent.EVENT_UPDATE);
sendS38BUFRequest(out_, id, file.getRecordIncrement(), data, offset, file.getRecordLength(), nullFieldValues);
out_.flush();
handleReply(file, "ddmS38UPDAT", null);
}
/**
* Updates the current record with the first record provided by the callback.
**/
public void updateCurrent(DDMFile file, DDMWriteCallback listener) throws IOException
{
int id = sendS38UPDATRequest(out_, file.getDCLNAM());
file.getEventBuffer().setEventType(DDMCallbackEvent.EVENT_UPDATE);
sendS38BUFRequest(file, out_, id, file.getRecordIncrement(), listener, file.getRecordLength(), 0, 1);
out_.flush();
handleReply(file, "ddmS38UPDAT", null);
}
/**
* Executes the specified CL command within the DDM host server job.
**/
public Message[] execute(final String command) throws IOException {
List messageList = executeReturnMessageList(command);
Message[] messages = new Message[messageList.size()];
for (int i =0; i < messages.length; i++) {
messages[i] = messageList.get(i);
}
return messages;
}
public List executeReturnMessageList(final String command) throws IOException
{
ArrayList messages = new ArrayList();
sendS38CMDRequest(out_, command);
out_.flush();
handleReply(null, "ddmS38CMD", null, messages);
return messages;
}
/**
* Removes the current record from the file.
**/
public void deleteCurrent(DDMFile file) throws IOException
{
sendS38DELRequest(out_, file.getDCLNAM());
out_.flush();
handleReply(file, "ddmS38DEL", null);
}
private void position(DDMFile file, int positionType) throws IOException
{
sendS38FEODRequest(out_, file.getDCLNAM(), positionType);
out_.flush();
handleReply(file, "ddmS38FEOD", null);
}
private void readType(DDMFile file, DDMReadCallback listener, int readType, boolean doPosition) throws IOException
{
sendS38GetRequest(out_, file.getDCLNAM(), file.getReadWriteType() == DDMFile.READ_WRITE, readType, doPosition);
out_.flush();
handleReply(file, "ddmS38Get", listener);
}
private void handleReply(final DDMFile file, final String ds, final DDMReadCallback listener) throws IOException
{
handleReply(file, ds, listener, null);
}
private void handleReply(final DDMFile file, final String ds, final DDMReadCallback listener, final List messages) throws IOException
{
if (DEBUG) System.out.println("---- HANDLE REPLY ----");
int length = in_.readShort();
boolean isContinued = (length > 0x7FFF);
if (isContinued)
{
length = length & 0x7FFF; // The last 2 bytes are the size of the next continued packet.
}
if (DEBUG) System.out.println("LENGTH: "+length);
if (DEBUG) System.out.println("isContinued? "+isContinued);
if (length < 10)
{
throw DataStreamException.badLength(ds, length);
}
int gdsID = in_.read();
int type = in_.read(); // bit mask
if (DEBUG) System.out.println("GDS, TYPE: "+gdsID+", "+type);
boolean isChained = (type & 0x40) != 0;
int correlation = in_.readShort();
int numRead = 6;
if (DEBUG) System.out.println("CHAINED: "+isChained);
DataStreamException exception = null;
while (numRead < length)
{
if (DEBUG) System.out.println(numRead+" vs "+length);
int ll = in_.readShort();
int cp = in_.readShort();
numRead += 4;
if (DEBUG) System.out.println("HANDLEREPLY: ll="+ll+", cp="+Integer.toHexString(cp));
if (cp == 0xD402) // S38IOFB
{
// Probably end of file or record not found.
boolean end = !isChained;
in_.skipBytes(length-numRead);
numRead += length-numRead;
while (isChained)
{
int localLength = in_.readShort();
if (localLength < 10)
{
throw DataStreamException.badLength(ds, localLength);
}
gdsID = in_.read();
type = in_.read(); // bit mask
isChained = (type & 0x40) != 0;
correlation = in_.readShort();
ll = in_.readShort();
cp = in_.readShort();
int localNumRead = 10;
numRead += 10;
if (DEBUG) System.out.println("Next is chained? "+isChained+", "+Integer.toHexString(cp));
if (cp == 0xD201) // S38MSGRM
{
int[] numMsgRead = new int[1];
Message msg = getMessage(in_, ll, numMsgRead);
numRead += numMsgRead[0];
localNumRead += numMsgRead[0];
if (DEBUG) System.out.println("Got message "+msg);
if (msg != null)
{
if (messages != null) messages.add(msg);
String id = msg.getID();
if (id != null)
{
if (id.equals("CPF5001") || id.equals("CPF5025"))
{
if (listener != null)
{
file.getEventBuffer().setEventType(DDMCallbackEvent.EVENT_READ);
listener.endOfFile(file.getEventBuffer());
}
end = true;
}
else if (id.equals("CPF5006"))
{
if (listener != null)
{
file.getEventBuffer().setEventType(DDMCallbackEvent.EVENT_READ);
listener.recordNotFound(file.getEventBuffer());
}
end = true;
}
else if (messages == null)
{
if (exception == null)
{
exception = DataStreamException.errorMessage(ds+"_chained", msg);
exception.fillInStackTrace();
}
else
{
exception.addMessage(msg);
}
end = true;
}
}
else if (messages == null)
{
if (exception == null)
{
exception = DataStreamException.errorMessage(ds+"_chained", msg);
exception.fillInStackTrace();
}
else
{
exception.addMessage(msg);
}
end = true;
}
}
}
else
{
in_.skipBytes(ll-4);
numRead += ll-4;
localNumRead += ll-4;
}
in_.skipBytes(localLength-localNumRead);
numRead += localLength-localNumRead;
}
if (!end)
{
throw DataStreamException.badReply(ds, cp);
}
}
else if (cp == 0xD405) // S38BUF
{
final boolean largeBuffer = ll > 0x7FFF;
if (DEBUG) System.out.println("LARGE BUFFER: "+largeBuffer);
int ioFeedbackOffset = largeBuffer ? in_.readInt() + 18 : ll + 10;
if (DEBUG) System.out.println("IOFB offset: "+ioFeedbackOffset);
numRead += largeBuffer ? 4 : 0;
if (listener == null)
{
int toSkip = ioFeedbackOffset - numRead - 4;
if (toSkip > 0)
{
in_.skipBytes(toSkip);
numRead += toSkip;
}
}
else
{
final int recordDataLength = file.getRecordLength();
final int recordIncrement = file.getRecordIncrement();
if (DEBUG) System.out.println("Record data length: "+recordDataLength);
if (DEBUG) System.out.println("Record increment: "+recordIncrement);
while (numRead+recordIncrement <= ioFeedbackOffset)
{
final byte[] recordData = file.getRecordDataBuffer();
boolean didExtraBytes = false;
if (isContinued && numRead+recordIncrement > length)
{
// Not enough bytes left.
int diff = length-numRead;
if (DEBUG) System.out.println("Not enough bytes to read another record. Length="+length+", numRead="+numRead+", so remaining="+diff);
int dataToRead = diff > recordData.length ? recordData.length : diff;
if (DEBUG) System.out.println("Data to read: "+dataToRead+" will make numRead: "+(numRead+dataToRead));
in_.readFully(recordData, 0, dataToRead);
numRead += dataToRead;
int remainingRecordData = recordData.length-dataToRead;
if (DEBUG) System.out.println("Remaining record data: "+remainingRecordData);
if (remainingRecordData > 0)
{
int nextPacketLength = in_.readShort();
if (DEBUG) System.out.println("Next packet length: "+nextPacketLength);
if (nextPacketLength <= 0x7FFF)
{
isContinued = false;
}
int extraLength = (nextPacketLength & 0x7FFF) - 2;
if (DEBUG) System.out.println("Current length: "+length+"; Still continued? "+isContinued+"; next packet length: "+extraLength);
length += extraLength;
if (DEBUG) System.out.println("New length: "+length);
in_.readFully(recordData, dataToRead, remainingRecordData);
numRead += remainingRecordData;
}
else
{
//TODO - This section can't be right, it's not handling the null field map.
diff -= recordData.length;
// This reads in the remaining data for the record, including the 2 bytes for the next packet length.
byte[] packetBuffer = file.getPacketBuffer();
in_.readFully(packetBuffer);
// if (DEBUG) System.out.println("JUST read "+packetBuffer.length+" bytes instead of "+diff);
numRead += packetBuffer.length;
// numRead += diff+2;
int nextPacketLength = ((packetBuffer[diff] & 0x00FF) << 8) | (packetBuffer[diff+1] & 0x00FF);
if (DEBUG) System.out.println("NEXT packet length: "+nextPacketLength);
if (nextPacketLength <= 0x7FFF)
{
isContinued = false;
}
int extraLength = (nextPacketLength & 0x7FFF); // - 2;
if (DEBUG) System.out.println("CURRENT length: "+length+"; Still continued? "+isContinued+"; next packet length: "+extraLength);
length += extraLength;
if (DEBUG) System.out.println("NEW length: "+length);
int relative = 0;
int recordNumber = -1;
if (diff < 3)
{
recordNumber = Conv.byteArrayToInt(packetBuffer, 4);
relative = 8;
}
else if (diff > 5)
{
recordNumber = Conv.byteArrayToInt(packetBuffer, 2);
relative = 6;
}
else
{
// The packet busted us in the middle of the record number.
int offset = 2;
int b1 = packetBuffer[offset++];
if (diff == offset) offset += 2;
int b2 = packetBuffer[offset++];
if (diff == offset) offset += 2;
int b3 = packetBuffer[offset++];
if (diff == offset) offset += 2;
int b4 = packetBuffer[offset++];
recordNumber = ((b1 & 0x00FF) << 24) |
((b2 & 0x00FF) << 16) |
((b3 & 0x00FF) << 8) |
(b4 & 0x00FF);
relative = offset;
}
if (DEBUG) System.out.println("Relative is "+relative+" and +5 for the null field map.");
// relative = file.getNullFieldByteMapOffset()-file.getRecordLength()-relative;
relative += 5;
// Read null field map.
final byte[] nullFieldMap = file.getNullFieldMap();
final boolean[] nullFieldValues = file.getNullFieldValues();
for (int i=0; i lenSkip) toSkip = lenSkip;
if (DEBUG) System.out.println("internal skip "+toSkip);
if (toSkip > 0)
{
in_.skipBytes(toSkip);
numRead += toSkip;
}
if (ll < 32768 && ll < file.getRecordIncrement()*file.getBatchSize())
{
//TODO: Have probably read all the records??
file.getEventBuffer().setEventType(DDMCallbackEvent.EVENT_READ);
listener.endOfFile(file.getEventBuffer());
}
}
// IOFB
if (DEBUG) System.out.println("Before IOFB, numRead = "+numRead+", length="+length);
int toSkip = length-numRead;
if (DEBUG) System.out.println("Skipping "+toSkip+" maybe");
if (toSkip > 0)
{
in_.skipBytes(toSkip);
numRead += toSkip;
}
while (isChained)
{
int localLength = in_.readShort();
if (DEBUG) System.out.println("In loop, length = "+localLength);
if (DEBUG) System.out.println("3isContinued? "+((localLength & 0x8000) != 0));
if (localLength < 10)
{
throw DataStreamException.badLength(ds+"_chained", localLength);
}
gdsID = in_.read();
type = in_.read(); // bit mask
isChained = (type & 0x40) != 0;
if (DEBUG) System.out.println("CHAIN2: "+isChained);
correlation = in_.readShort();
ll = in_.readShort();
cp = in_.readShort();
if (DEBUG) System.out.println("CP in chain: "+Integer.toHexString(cp));
int localNumRead = 10;
numRead += 10;
if (cp == 0xD201) // S38MSGRM
{
int[] numMsgRead = new int[1];
Message msg = getMessage(in_, ll, numMsgRead);
localNumRead += numMsgRead[0];
numRead += numMsgRead[0];
if (msg != null)
{
if (messages != null) messages.add(msg);
String id = msg.getID();
if (id != null)
{
if (id.equals("CPF5001") || id.equals("CPF5025"))
{
if (listener != null)
{
file.getEventBuffer().setEventType(DDMCallbackEvent.EVENT_READ);
listener.endOfFile(file.getEventBuffer());
}
}
else if (id.equals("CPF5006"))
{
if (listener != null)
{
file.getEventBuffer().setEventType(DDMCallbackEvent.EVENT_READ);
listener.recordNotFound(file.getEventBuffer());
}
}
else if (messages == null)
{
throw DataStreamException.errorMessage(ds+"_chained", msg);
}
}
else if (messages == null)
{
throw DataStreamException.errorMessage(ds+"_chained", msg);
}
}
}
else if (cp == 0xD402) // S38IOFB
{
if (DEBUG) System.out.println("SKipping "+(localLength-localNumRead));
in_.skipBytes(localLength-localNumRead);
numRead += (localLength-localNumRead);
localNumRead = localLength;
}
else
{
throw DataStreamException.badReply(ds+"_chained", cp);
}
if (DEBUG) System.out.println("Skipping extra "+(localLength-localNumRead));
in_.skipBytes(localLength-localNumRead);
numRead += localLength-localNumRead;
}
if (DEBUG) System.out.println("Should be at end: "+numRead+" and "+length);
}
else if (cp == 0xD201) // S38MSGRM
{
int[] numMsgRead = new int[1];
Message msg = getMessage(in_, ll, numMsgRead);
numRead += numMsgRead[0];
if (msg != null)
{
if (messages != null)
{
messages.add(msg);
}
else
{
throw DataStreamException.errorMessage(ds, msg);
}
}
int toSkip = length-numRead;
if (DEBUG) System.out.println("Skipping "+toSkip+" mmaybe");
if (toSkip > 0)
{
in_.skipBytes(toSkip);
numRead += toSkip;
}
while (isChained)
{
int localLength = in_.readShort();
if (DEBUG) System.out.println("In loop, length = "+localLength);
if (DEBUG) System.out.println("3isContinued? "+((localLength & 0x8000) != 0));
if (localLength < 10)
{
throw DataStreamException.badLength(ds+"_chained", localLength);
}
gdsID = in_.read();
type = in_.read(); // bit mask
isChained = (type & 0x40) != 0;
if (DEBUG) System.out.println("CHAIN2: "+isChained);
correlation = in_.readShort();
ll = in_.readShort();
cp = in_.readShort();
if (DEBUG) System.out.println("CP in chain: "+Integer.toHexString(cp));
int localNumRead = 10;
numRead += 10;
if (cp == 0xD201) // S38MSGRM
{
numMsgRead = new int[1];
msg = getMessage(in_, ll, numMsgRead);
localNumRead += numMsgRead[0];
numRead += numMsgRead[0];
if (msg != null)
{
if (messages != null) messages.add(msg);
String id = msg.getID();
if (id != null)
{
if (id.equals("CPF5001") || id.equals("CPF5025"))
{
if (listener != null)
{
file.getEventBuffer().setEventType(DDMCallbackEvent.EVENT_READ);
listener.endOfFile(file.getEventBuffer());
}
}
else if (id.equals("CPF5006"))
{
if (listener != null)
{
file.getEventBuffer().setEventType(DDMCallbackEvent.EVENT_READ);
listener.recordNotFound(file.getEventBuffer());
}
}
else if (messages == null)
{
throw DataStreamException.errorMessage(ds+"_chained", msg);
}
}
else if (messages == null)
{
throw DataStreamException.errorMessage(ds+"_chained", msg);
}
}
}
else if (cp == 0xD402) // S38IOFB
{
if (DEBUG) System.out.println("SKipping "+(localLength-localNumRead));
in_.skipBytes(localLength-localNumRead);
numRead += (localLength-localNumRead);
localNumRead = localLength;
}
else
{
throw DataStreamException.badReply(ds+"_chained", cp);
}
if (DEBUG) System.out.println("Skipping extra "+(localLength-localNumRead));
in_.skipBytes(localLength-localNumRead);
numRead += localLength-localNumRead;
}
}
else
{
throw DataStreamException.badReply(ds, cp);
}
}
in_.end();
if (exception != null)
{
throw exception;
}
}
// The declared name is an 8-byte unique handle that DDM will use to quickly access the file.
// I assume it is unique within a given connection. It must be EBCDIC and blank-padded.
// Which means, if we use an 8-digit number, we can only have 100,000,000 files open simultaneously. :-)
private byte[] generateDCLNAM()
{
final byte[] dclName = new byte[8];
int num = dclNamCounter_++; // No need to synchronize, this class isn't threadsafe anyway.
// Hooray for loop-unrolling and temp variables!
// This works great with the IBM 1.5 compiler and JIT. Not so much with Sun 1.4.
int mod = num % 10;
dclName[7] = (byte)(mod + 0xF0);
num = num / 10;
mod = num % 10;
dclName[6] = (byte)(mod + 0xF0);
num = num == 0 ? 0 : num / 10;
mod = num == 0 ? 0 : num % 10;
dclName[5] = (byte)(mod + 0xF0);
num = num == 0 ? 0 : num / 10;
mod = num == 0 ? 0 : num % 10;
dclName[4] = (byte)(mod + 0xF0);
num = num == 0 ? 0 : num / 10;
mod = num == 0 ? 0 : num % 10;
dclName[3] = (byte)(mod + 0xF0);
num = num == 0 ? 0 : num / 10;
mod = num == 0 ? 0 : num % 10;
dclName[2] = (byte)(mod + 0xF0);
num = num == 0 ? 0 : num / 10;
mod = num == 0 ? 0 : num % 10;
dclName[1] = (byte)(mod + 0xF0);
num = num == 0 ? 0 : num / 10;
mod = num == 0 ? 0 : num % 10;
dclName[0] = (byte)(mod + 0xF0);
return dclName;
}
private void sendS38CMDRequest(HostOutputStream out, String command) throws IOException
{
final byte[] commandBytes = Conv.stringToEBCDICByteArray37(command);
out.writeShort(14+commandBytes.length); // Length.
out.write(0xD0); // SNA GDS architecture ID.
out.write(1); // Format ID.
out.writeShort(newCorrelationID());
out.writeShort(8+commandBytes.length); // S38CMD LL.
out.writeShort(0xD006); // S38CMD CP.
out.writeShort(4+commandBytes.length); // S38CMDST LL.
out.writeShort(0xD103); // S38CMDST CP.
out.write(commandBytes);
}
private void sendS38DELRequest(final HostOutputStream out, final byte[] dclNam) throws IOException
{
out.writeInt(0x0016D001); // Combined length, SNA GDS arch ID, format ID.
out.writeShort(newCorrelationID());
out.writeInt(0x0010D007); // Combined S38DEL LL and CP.
out.writeInt(0x000C1136); // Combined DCLNAM LL and CP.
out.write(dclNam, 0, 8);
}
private int sendS38UPDATRequest(HostOutputStream out, byte[] dclNam) throws IOException
{
out.writeInt(0x001ED051); // Combined length, SNA GDS arch ID, and format ID.
int id = newCorrelationID();
out.writeShort(id);
out.writeInt(0x0018D019); // Combined S38UPDAT LL and CP.
out.writeInt(0x000C1136); // Combined DCLNAM LL and CP.
out.write(dclNam, 0, 8);
out.writeInt(0x0008D119); // Combined S38OPTL LL and CP.
// out.write(33); // Type of read (current).
// out.write(3); // Share - update norm.
// out.write(1); // Data - don't retrieve record, ignore deleted records.
// out.write(7); // Operation - update.
out.writeInt(0x21030107); // Combined options.
return id;
}
private void sendS38PUTMRequest(HostOutputStream out, byte[] dclNam, final int correlationID) throws IOException
{
out.writeInt(0x0016D051); // Combined length, SNA GDS arch ID, and format ID.
out.writeShort(correlationID);
out.writeInt(0x0010D013); // Combined S38PUTM LL and CP.
out.writeInt(0x000C1136); // Combined DCLNAM LL and CP.
out.write(dclNam, 0, 8);
}
private void sendS38BUFRequest(HostOutputStream out, final int correlationID, final int recordIncrement, final byte[] data, final int offset, final int length, final boolean[] nullFieldValues) throws IOException
{
// Now send S38BUF with data in it.
out.writeShort(recordIncrement+10); // Length.
out.writeShort(0xD003); // Combined SNA GDS arch ID and format ID.
out.writeShort(correlationID);
out.writeShort(recordIncrement+4); // S38BUF LL.
out.writeShort(0xD405); // S38BUF CP.
out.write(data, offset, length);
final int remaining = recordIncrement - length;
final boolean doNullFields = nullFieldValues != null;
for (int i=0; i
© 2015 - 2025 Weber Informatics LLC | Privacy Policy