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 Show documentation
Show all versions of jt400 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