All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jtopenlite.com.ibm.jtopenlite.ddm.DDMConnection Maven / Gradle / Ivy

There is a newer version: 20.0.7
Show newest version
///////////////////////////////////////////////////////////////////////////////
//
// 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 - 2024 Weber Informatics LLC | Privacy Policy