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

src.com.ibm.as400.access.AS400FileImplNative Maven / Gradle / Ivy

There is a newer version: 11.1
Show newest version
///////////////////////////////////////////////////////////////////////////////
//                                                                             
// JTOpen (IBM Toolbox for Java - OSS version)                              
//                                                                             
// Filename: AS400FileImplNative.java
//                                                                             
// The source code contained herein is licensed under the IBM Public License   
// Version 1.0, which has been approved by the Open Source Initiative.         
// Copyright (C) 1997-2004 International Business Machines Corporation and     
// others. All rights reserved.                                                
//                                                                             
///////////////////////////////////////////////////////////////////////////////
//
// @A3 - 10/30/2007 - The NULL Field Byte Map array must be set based on the 
//       maximum number of fields for any format in a given file.  Refer to 
//       more detailed explanation for related changes in 
//       DDMObjectDataStream.java (@A1)
//       There is also a related change in AS400FileImplRemote.java
//                                                                             
///////////////////////////////////////////////////////////////////////////////
package com.ibm.as400.access;

import java.beans.PropertyVetoException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal; //@C1A
import java.util.Vector; //@C1A

//@C0C: We now extend AS400FileImplBase.
class AS400FileImplNative extends AS400FileImplBase
{
  private static final String CLASSNAME = "com.ibm.as400.access.AS400FileImplNative";
  static
  {
    if (Trace.traceOn_) Trace.logLoadPath(CLASSNAME);
  }

    // File handle.
    transient int handle_;
    // Static synchronization variable for command execution
    static String synch_execute_ = "";
    // Static synchronization variable for open/close of files.
    // The qyjsprl.C smalltalk C module keeps track of the handles allocated
    // and freed for files globally.  Therefore we synchronize opens and closes
    // at the class level for processes using RLA
    static Object synch_open_close_ = new Object() ;

    static
    {
    	   NativeMethods.loadNativeLibraryQyjspart(); 
        
        try
        {
            resetStaticStorage(); //@E3A
        }
        catch(Throwable t) //@E3A In case they don't have the new service program change to match.
        {
            if (Trace.isTraceOn() && Trace.isTraceWarningOn()) //@E3A
            {
                Trace.log(Trace.WARNING, "Exception occurred while resetting static storage for DDM: ", t); //@E3A
            }
        }
    }

    //@C1A - Need this for the new createDDSSourceFile() method.
    public AS400FileImplNative()
    {
        isNative_ = true; //@E2A
    }

    /**
     *Closes the file on the server.
     *@exception AS400Exception If the server returns an error message.
     *@exception AS400SecurityException If a security or authority error occurs.
     *@exception InterruptedException If this thread is interrupted.
     *@exception IOException If an error occurs while communicating with the
     *server.
     **/
    public void close()
      throws AS400Exception, AS400SecurityException, InterruptedException,  IOException
    {
        super.close(); //@C0A

        byte[] swapToPH = new byte[12];
        byte[] swapFromPH = new byte[12];
        boolean didSwap = system_.swapTo(swapToPH, swapFromPH);
        try
        {
            synchronized(synch_open_close_)
            {
                // Close the file.
                closeNtv(handle_);
            }
        }
        catch(NativeException e)
        {
            // Parse the message feedback data and throw AS400Exception
            throw new AS400Exception(parseMsgFeedback(e.data));
        }
        finally
        {
            if (didSwap) system_.swapBack(swapToPH, swapFromPH);
        }
    }

    /**
     *Closes the file on the server.
     *@param handle the file handle.
     **/
    native void closeNtv(int handle)
      throws NativeException;

    /**
     *Commits all transactions since the last commit boundary.  Invoking this
     *method will cause all transactions under commitment control for this
     *connection to be committed.  This means that any AS400File object opened
     *under this connection, for which a commit lock level was specified, will
     *have outstanding transactions committed.  If commitment control has not been
     *started for the connection, no action is taken.
*The server to which to connect must be set prior to invoking this *method. *@exception AS400Exception If the server returns an error message. *@exception AS400SecurityException If a security or authority error occurs. *@exception InterruptedException If this thread is interrupted. *@exception IOException If an error occurs while communicating with the *server. **/ public void commit() throws AS400Exception, AS400SecurityException, InterruptedException, IOException { byte[] swapToPH = new byte[12]; byte[] swapFromPH = new byte[12]; boolean didSwap = system_.swapTo(swapToPH, swapFromPH); try { // Commit transactions under commitment control. commitNtv(); } catch(NativeException e) { // Parse the message feedback data and throw AS400Exception throw new AS400Exception(parseMsgFeedback(e.data)); } finally { if (didSwap) system_.swapBack(swapToPH, swapFromPH); } } /** *Commits all transactions since the last commit boundary. Invoking this *method will cause all transactions under commitment control for this *connection to be committed. This means that any AS400File object opened *under this connection, for which a commit lock level was specified, will *have outstanding transactions committed. If commitment control has not been *started for the connection, no action is taken.
*The server to which to connect must be set prior to invoking this *method. **/ native void commitNtv() throws NativeException; //@C1M 7/16/99 - Moved this code out of ImplBase to here /** *Creates the DDS source file to be used to create a physical file based on a user *supplied RecordFormat.
*The name of the file and the server to which to connect must be set prior *to invoking this method. *@see AS400File#AS400File(com.ibm.as400.access.AS400, java.lang.String) *@see AS400File#setPath *@see AS400File#setSystem *@param recordFormat The record format to describe in the DDS source file. *@param altSeq The value to be specified for the file-level keyword ALTSEQ. If no *value is to be specified, null may be specified. *@param ccsid The value to be specified for the file-level keyword CCSID. If no *value is to be specified, null may be specified. *@param order The value to be specified to indicate in which order records are to be *retrieved from the file. Valid values are one of the following file-level keywords: *
    *
  • FIFO - First in, first out *
  • LIFO - Last in, first out *
  • FCFO - First changed, first out *
*If no ordering value is to be specified, null may be specified. *@param ref The value to be specified for the file-level keyword REF. If no *value is to be specified, null may be specified. *@param unique Indicates if the file-level keyword UNIQUE is to be specified. True *indicates that the UNIQUE keyword should be specified; false indicates that it *should not be specified. *@param format The value to be specified for the record-level keyword FORMAT. If no *value is to be specified, null may be specified. *@param text The value to be specified for the record-level keyword TEXT. If no *value is to be specified, null may be specified. *@exception AS400Exception If the server returns an error message. *@exception AS400SecurityException If a security or authority error occurs. *@exception ConnectionDroppedException If the connection is dropped unexpectedly. *@exception InterruptedException If this thread is interrupted. *@exception IOException If an error occurs while communicating with the server. *@exception ServerStartupException If the host server cannot be started. **/ public synchronized void createDDSSourceFile(RecordFormat recordFormat, String altSeq, String ccsid, String order, String ref, boolean unique, String format, String text) throws AS400Exception, AS400SecurityException, InterruptedException, IOException { // Create the source physical file to hold the DDS source. Note that we create the // file in library QTEMP. Each server job has its own QTEMP library which is created // when the job starts and is deleted when the job ends. Using QTEMP allows // the file to be created regardless of the user's authority and also eliminates // name collision problems when different jobs are creating files from a record // format. AS400Message[] msgs = execute("QSYS/CRTSRCPF FILE(QTEMP/JT400DSSRC) MBR(*FILE) TEXT('JT400 TEMPORARY DDS SOURCE FILE')"); //@B0C if (msgs.length > 0) { if (msgs[0].getID().equals("CPF5813")) { // File already exists from a previous create; clear it. msgs = execute("QSYS/CLRPFM QTEMP/JT400DSSRC"); //@B0C if (msgs.length > 0) { if (!msgs[0].getID().equals("CPC3101")) // "member cleared" { // Clear failed. Throw exception. Trace.log(Trace.ERROR, "QSYS/CLRPFM QTEMP/JT400DSSRC"); throw new AS400Exception(msgs); } } else { Trace.log(Trace.ERROR, "No messages were returned from QSYS/CLRPFM QTEMP/JT400DSSRC"); throw new InternalErrorException("QTEMP/JT400DSSRC", InternalErrorException.UNKNOWN); } } else if (!msgs[0].getID().equals("CPC7301")) { // File does not exist and we were unable to create; throw exception // (CPC7301 = Successful create) Trace.log(Trace.ERROR, "QSYS/CRTSRCPF FILE(QTEMP/JT400DSSRC) MBR(*FILE) TEXT('JT400 TEMPORARY DDS SOURCE FILE')"); throw new AS400Exception(msgs); } } else { // No messages. This shouldn't happen. Trace.log(Trace.ERROR, "No messages from server."); throw new InternalErrorException(InternalErrorException.UNKNOWN); } ///////////////////////////////////////////////////////////////////////////////// // Create the records to be written to the file. These records will contain the // DDS based on the supplied RecordFormat object. ///////////////////////////////////////////////////////////////////////////////// // Create a RecordFormat object which describes the record format of a source // physical file. RecordFormat srcRF = new RecordFormat("JT400DSSRC"); srcRF.addFieldDescription(new ZonedDecimalFieldDescription(new AS400ZonedDecimal(6, 2), "SRCSEQ")); srcRF.addFieldDescription(new ZonedDecimalFieldDescription(new AS400ZonedDecimal(6, 0), "SRCDAT")); //@C2 - This is how it should be done, but it will work the deprecated way because // we are running on the 400. //@C2 AS400Text text80 = new AS400Text(80, system_.getCcsid(), system_); //@C2A //@C2 if (converter_ == null) setConverter(); //@C2A //@C2 text80.setConverter(converter_); //@C2A srcRF.addFieldDescription(new CharacterFieldDescription(new AS400Text(80, system_.getCcsid()), "SRCDTA")); Vector lines = new Vector(); // Contains DDS lines to write to source file String line; // A single line of DDS source // Create line(s) for any file level keywords - file level keywords must precede // the line specifying the record format name. if (altSeq != null) { line = STR44 + "ALTSEQ(" + altSeq + ")"; lines.addElement(line); } if (ccsid != null) { line = STR44 + "CCSID(" + ccsid + ")"; lines.addElement(line); } if (order != null) { line = STR44 + order; lines.addElement(line); } if (ref != null) { line = STR44 + "REF(" + ref + ")"; lines.addElement(line); } if (unique) { line = STR44 + "UNIQUE"; lines.addElement(line); } // Create line for the record format name line = STR16 + "R "; // The record format name cannot exceed 10 characters and must be in upper case if (recordFormat.getName().length() > 10) { if (Trace.isTraceOn() && Trace.isTraceWarningOn()) { Trace.log(Trace.WARNING, "Record format name '"+recordFormat.getName()+"' too long. Using '"+recordFormat.getName().substring(0,10)+"' instead."); } line += recordFormat.getName().substring(0, 10); } else { line += recordFormat.getName(); } lines.addElement(line); // Create line(s) for any record level keywords. The record level keywords // must be on the same line or on the lines immediately following the record // format line. if (format != null) { line = STR44 + "FORMAT(" + format + ")"; lines.addElement(line); } if (text != null) { if (text.length() > 32) { // Text exceeds length left on line - need to continue on next line line = STR44 + "TEXT('" + text.substring(0, 33) + "-"; lines.addElement(line); // Add the remainder of the TEXT keyword line = STR44 + text.substring(34) + "')"; lines.addElement(line); } else { // Text fits on one line line = STR44 + "TEXT('" + text + "')"; lines.addElement(line); } } // Create lines for each field description and any keywords for the field int numberOfFields = recordFormat.getNumberOfFields(); FieldDescription f = null; String ddsDesc; String[] dds = null; int length; int beginningOffset; for (int i = 0; i < numberOfFields; ++i) { f = recordFormat.getFieldDescription(i); // Specify the DDS description of the field. The DDS description returned // from FieldDescription contains the field level keywords as well as the // description of the field. It is formatted properly for DDS except for // the preceding blanks. Therefore, we add 18 blanks so that the field // description starts in column 19 of the line. dds = f.getDDSDescription(); // Add the fixed portion of the DDS description for the field to the vector ddsDesc = STR18 + dds[0]; lines.addElement(ddsDesc); // Add lines containing field level keywords for (int j = 1; j < dds.length; ++j) { ddsDesc = STR44 + dds[j]; length = ddsDesc.length(); beginningOffset = 0; if (length > 80) { // Need to continue the line on the next line line = ddsDesc.substring(beginningOffset, 79) + "-"; lines.addElement(line); length -= 79; beginningOffset = 79; line = STR44 + ddsDesc.substring(beginningOffset); lines.addElement(line); } else { // It all fits on one line lines.addElement(ddsDesc); } } } // Create lines for key fields and key field level keywords numberOfFields = recordFormat.getNumberOfKeyFields(); for (int i = 0; i < numberOfFields; ++i) { f = recordFormat.getKeyFieldDescription(i); // Specify the name of the field line = STR16 + "K "; line += f.getDDSName(); lines.addElement(line); // Specify any key field level keywords String[] keyFuncs = f.getKeyFieldFunctions(); if (keyFuncs != null) { for (short j = 0; j < keyFuncs.length; ++j) { line = STR44 + keyFuncs[j]; lines.addElement(line); } } } // Create an array of records representing each line to be written // to the file. Record[] records = new Record[lines.size()]; for (int i = 0; i < records.length; ++i) { records[i] = srcRF.getNewRecord(); records[i].setField("SRCSEQ", new BigDecimal(i)); records[i].setField("SRCDAT", new BigDecimal(i)); records[i].setField("SRCDTA", lines.elementAt(i)); } // Open the DDS source file and write the records. We will write all the records // at one time, so we specify a blocking factor of records.length on the open(). //@C1 - This is why we had to move this code out of the ImplBase, so we can instantiate. AS400FileImplNative src = null; try { src = new AS400FileImplNative(); src.setAll(system_, "/QSYS.LIB/QTEMP.LIB/JT400DSSRC.FILE", srcRF, false, false, false); src.openFile2(AS400File.WRITE_ONLY, records.length, AS400File.COMMIT_LOCK_LEVEL_NONE, false); src.write(records); // Close the file src.close(); } catch(Exception e) { // log error and rethrow exception Trace.log(Trace.ERROR, "Unable to write records to DDS source file:"+e); try { if (src != null) { src.close(); } } catch(Exception e1) { // Ignore it; we will throw the original exception } if (e instanceof AS400Exception) { throw (AS400Exception) e; } else { Trace.log(Trace.ERROR, e); throw new InternalErrorException(InternalErrorException.UNKNOWN, e); } } } /** *Deletes the record at the current cursor position. *@exception AS400Exception If the server returns an error message. *@exception AS400SecurityException If a security or authority error occurs. *@exception InterruptedException If this thread is interrupted. *@exception IOException If an error occurs while communicating with the server. **/ public void deleteCurrentRecord() throws AS400Exception, AS400SecurityException, InterruptedException, IOException { byte[] optl = {0x00, SHR_READ_NORM_RLS, DATA_DTA_DTARCD, 0x08}; byte[] swapToPH = new byte[12]; byte[] swapFromPH = new byte[12]; boolean didSwap = system_.swapTo(swapToPH, swapFromPH); try { // Delete the current record. deleteCurrentRecordNtv(handle_, optl); } catch(NativeException e) { // Parse the message feedback data and throw AS400Exception throw new AS400Exception(parseMsgFeedback(e.data)); } finally { if (didSwap) system_.swapBack(swapToPH, swapFromPH); } } /** *Deletes the record at the current cursor position. The file must be open and *the cursor must be positioned on an active record. *@param handle the file handle *@exception AS400Exception If the server returns an error message. *@exception AS400SecurityException If a security or authority error occurs. *@exception InterruptedException If this thread is interrupted. *@exception IOException If an error occurs while communicating with the server. **/ native void deleteCurrentRecordNtv(int handle, byte[] optl) throws NativeException; /** *Executes a command on the server. *@param cmd the command *@exception AS400SecurityException If a security or authority error occurs. *@exception InterruptedException If this thread is interrupted. *@exception IOException If an error occurs while communicating with the server. **/ public AS400Message[] execute(String cmd) throws AS400SecurityException, InterruptedException, IOException { if (converter_ == null) setConverter(); //@C2A // Execute the command. // Note the execution of the command is synchronized on the static // variable synch_execute_ BytesWithOffset data = null; byte[] swapToPH = new byte[12]; byte[] swapFromPH = new byte[12]; boolean didSwap = system_.swapTo(swapToPH, swapFromPH); try { synchronized(synch_execute_) { data = new BytesWithOffset(executeNtv(converter_.stringToByteArray(cmd))); } } finally { if (didSwap) system_.swapBack(swapToPH, swapFromPH); } // Parse the message feedback data. AS400Message[] msgs = parseMsgFeedback(data.data_); return msgs; } /** *Executes a command on the server. *Submits the command string via the "Process Commands" (QCAPCMD) API. *Note: Although QCAPCMD is threadsafe, it "should not be used to run a command that is not threadsafe in a job that has multiple threads". *@param cmd the command *@return the message feedback data **/ native byte[] executeNtv(byte[] cmd); //@C0A /** *Prepare this object for garbage collection. *@exception Throwable If an exception is thrown while cleaning up. **/ public void finalization() throws Throwable { // This method does nothing. It is here because finalization() is // declared abstract in AS400FileImplBase. } /** *Changes the position of the file cursor to either before the first *record or after the last record. *@param handle the file handle *@param optl the options list **/ native void forceEndOfData(int handle, byte[] optl) throws NativeException; /** *Read record(s) from the file. Does not screen out CPF5001, CPF5006. *@param handle the file handle *@param optl option list *@param ctll control list *@param length length of all record data *@return message feedback data, I/O feedback data, record data. **/ native byte[] getForPosition(int handle, byte[] optl, byte[] ctll, int length) throws NativeException; /** *Read record(s) from the file. Screens out CPF5001, CPF5006 *@param handle the file handle *@param optl option list *@param ctll control list *@param length length of all record data *@return message feedback data, I/O feedback data, record data. **/ native byte[] getForRead(int handle, byte[] optl, byte[] ctll, int length) throws NativeException; /** *Read record(s) from the file by record number. Does not screen out *CPF5001, CPF5006. *@param handle the file handle *@param optl option list *@param ctll control list *@param length length of all record data *@return message feedback data, I/O feedback data, record data. **/ native byte[] getdForPosition(int handle, byte[] optl, byte[] ctll, int length) throws NativeException; /** *Read record(s) from the file by record number. Screens out CPF5001, *CPF5006. *@param handle the file handle *@param optl option list *@param ctll control list *@param length length of all record data *@return message feedback data, I/O feedback data, record data. **/ native byte[] getdForRead(int handle, byte[] optl, byte[] ctll, int length) throws NativeException; /** *Read record(s) from the file by key. Does not screen out CPF5001, *CPF5006. *@param handle the file handle *@param optl option list *@param ctll control list *@param length length of all record data *@return message feedback data, I/O feedback data, record data. **/ native byte[] getkForPosition(int handle, byte[] optl, byte[] ctll, int length) throws NativeException; /** *Read record(s) from the file by key. Screens out CPF5001, CPF5006. *@param handle the file handle *@param optl option list *@param ctll control list *@param length length of all record data *@return message feedback data, I/O feedback data, record data. **/ native byte[] getkForRead(int handle, byte[] optl, byte[] ctll, int length) throws NativeException; /** *Opens the file. *@param openType *@param bf blocking factor *@param access The type of file access for which to open the file. *@return the open feedback data *@exception AS400Exception If the server returns an error message. *@exception AS400SecurityException If a security or authority error occurs. *@exception ConnectionDroppedException If the connection is dropped unexpectedly. *@exception InterruptedException If this thread is interrupted. *@exception IOException If an error occurs while communicating with the server. *@exception ServerStartupException If the host server cannot be started.. **/ public DDMS38OpenFeedback openFile(int openType, int bf, String access) throws AS400Exception, AS400SecurityException, InterruptedException, IOException { // Create the user file control block. byte[] ufcb = createUFCB(openType, bf, access, true); BytesWithOffset data = null; byte[] swapToPH = new byte[12]; byte[] swapFromPH = new byte[12]; boolean didSwap = system_.swapTo(swapToPH, swapFromPH); try { synchronized(synch_open_close_) { // Open the file. data = new BytesWithOffset(openFileNtv(ufcb)); } } catch(NativeException e) { // Parse the message feedback data and throw AS400Exception throw new AS400Exception(parseMsgFeedback(e.data)); } finally { if (didSwap) system_.swapBack(swapToPH, swapFromPH); } // Parse the message feedback data. AS400Message[] msgs = parseMsgFeedback(data); // Parse the file handle. handle_ = BinaryConverter.byteArrayToInt(data.data_, data.offset_); data.offset_ += 4; // Throw an exception if the open failed and there are no AS400Messages if (handle_ <= 0) { Trace.log(Trace.ERROR, "Invalid handle returned from QYSTRART"); throw new InternalErrorException(InternalErrorException.UNKNOWN, handle_); } // Log warning messages if the open was successful (handle_ > 0) // and there are AS400Messages if (msgs.length > 0) { if (Trace.isTraceOn() && Trace.isTraceWarningOn()) { Trace.log(Trace.WARNING, "AS400FileImplNative.openFile:"); for (int i = 0; i < msgs.length; ++i) { Trace.log(Trace.WARNING, msgs[i].toString()); } } } // Parse the open feedback data. openFeedback_ = new LocalOpenFeedback(system_, //@C0C data.data_, data.offset_); return openFeedback_; } /** *Opens the file. *@param ufcb user file control block. *@return message feedback data, file handle, and open feedback data *(in that order) **/ native byte[] openFileNtv(byte[] ufcb) throws NativeException; /** *Parse the record data into records. *@param iofb I/O feedback data *@param data record data and offset. *@return the records contained in the record data **/ private Record[] parseRecordData(LocalIOFB iofb, BytesWithOffset data) throws UnsupportedEncodingException { int numberOfRecords = iofb.getNumberOfRecordsReturned(); Record[] records = new Record[numberOfRecords]; // Determine the null byte field map offset. When we opened the file, the // the S38OPNFB reply contained the offset of the null byte field map in a record. int nullFieldMapOffset = openFeedback_.getNullFieldByteMapOffset(); // Determine the number of fields in a record from the RecordFormat for this file. int numFields = recordFormat_.getNumberOfFields(); //@C0C // If the file has null capable fields, we will need to check the null byte field // map and set the fields within the Record object as appropriate. boolean isNullCapable = openFeedback_.isNullCapable(); for (int i = 0; i < numberOfRecords; ++i, data.offset_ += openFeedback_.getRecordIncrement()) { // Create the next record. records[i] = recordFormat_.getNewRecord(data.data_, //@C0C data.offset_); // Set any null fields to null if (isNullCapable) { for (int j = 0; j < numFields; ++j) { // 0xF1 = field is null, 0xF0 = field is not null if (data.data_[nullFieldMapOffset + j + data.offset_] == (byte)0xF1) { records[i].setField(j, null); } } } // Set the record number. The record number is two bytes after the end // of the record data and is four bytes long. try { records[i].setRecordNumber(BinaryConverter.byteArrayToInt(data.data_, data.offset_ + iofb.getRecordLength() + 2)); } catch(PropertyVetoException e) { // We created the Record objects. There is no one to veto anything } // so this is here to quite the compiler } return records; } /** *Parse the message feedback data into an array of AS400Message objects. *@param data the message feedback data and offset. *@return array of AS400Message objects (zero length array if no messages are available **/ private AS400Message[] parseMsgFeedback(byte[] data) throws IOException { AS400Message[] msgs = new AS400Message[0]; // Determine if any messages are available. Record msgFB = (new MessageFBFormat()).getNewRecord(data); Short messagesOccurred = (Short) msgFB.getField("messagesOccurred"); if (messagesOccurred.intValue() == 1) { // Determine the number of messages. int numMsgs = ((Short) msgFB.getField("numberOfMessages")).intValue(); if (numMsgs == 0) { return msgs; } // Loop through the array of message feedback data structures, // building an AS400Message object for each one. msgs = new AS400Message[numMsgs]; MessageFBDataFormat msgFBDataFormat = new MessageFBDataFormat(system_.getCcsid()); //@C0C Object[] feedbackData = (Object[]) msgFB.getField("feedbackData"); for (int i = 0; i < numMsgs; i++) { // Extract the severity, type, ID, and text from the message // feedback. Record msg = msgFBDataFormat.getNewRecord((byte[]) feedbackData[i]); int severity = ((Integer) msg.getField("severityCode")).intValue(); String typeString = (String)msg.getField("messageType"); char[] typeChars = typeString.toCharArray(); int type = (typeChars[0] & 0x0F) * 10 + (typeChars[1] & 0x0F); String id = (String) msg.getField("messageID"); int textLength = ((Integer) msg.getField("messageTextLength")).intValue(); int substitutionDataLength = ((Integer) msg.getField("replacementTextLength")).intValue(); //@B0A: We don't use the messageText field of the record format because // it hardcodes a length of 256 bytes. If the messageText is shorter // than 256 bytes, then we are translating junk bytes, which can // cause problems when running under a double-byte ccsid where // weird characters would not translate correctly. // Instead, we use the messageTextLength field to determine how // long the messageText is and translate it ourselves using a // CharConverter object. byte[] arr = (byte[])feedbackData[i]; //@B0A String text = (new CharConverter(system_.getCcsid())).byteArrayToString(arr, 112 + substitutionDataLength, textLength); //@B0A //@C0C //@B0D String text = //@B0D ((String) msg.getField("messageText")).substring(0, textLength); // Construct an AS400Message. msgs[i] = new AS400Message(id, text); msgs[i].setSeverity(severity); msgs[i].setType(type); } } return msgs; } /** *Parse the message feedback data into an array of AS400Message objects. *@param data the message feedback data and offset. *@return array of AS400Message objects (zero length array if no messages are available **/ private AS400Message[] parseMsgFeedback(BytesWithOffset data) throws IOException { AS400Message[] msgs = new AS400Message[0]; // Determine if any messages are available. Record msgFB = (new MessageFBFormat()).getNewRecord(data.data_, data.offset_); data.offset_ += msgFB.getRecordLength(); Short messagesOccurred = (Short) msgFB.getField("messagesOccurred"); if (messagesOccurred.intValue() == 1) { // Determine the number of messages. int numMsgs = ((Short) msgFB.getField("numberOfMessages")).intValue(); if (numMsgs == 0) { return msgs; } // Loop through the array of message feedback data structures, // building an AS400Message object for each one. msgs = new AS400Message[numMsgs]; MessageFBDataFormat msgFBDataFormat = new MessageFBDataFormat(system_.getCcsid()); //@C0C Object[] feedbackData = (Object[]) msgFB.getField("feedbackData"); for (int i = 0; i < numMsgs; i++) { // Extract the severity, type, ID, and text from the message // feedback. Record msg = msgFBDataFormat.getNewRecord((byte[]) feedbackData[i]); int severity = ((Integer) msg.getField("severityCode")).intValue(); String typeString = (String)msg.getField("messageType"); char[] typeChars = typeString.toCharArray(); int type = (typeChars[0] & 0x0F) * 10 + (typeChars[1] & 0x0F); String id = (String) msg.getField("messageID"); int textLength = ((Integer) msg.getField("messageTextLength")).intValue(); int substitutionDataLength = ((Integer) msg.getField("replacementTextLength")).intValue(); //@B0A: We don't use the messageText field of the record format because // it hardcodes a length of 256 bytes. If the messageText is shorter // than 256 bytes, then we are translating junk bytes, which can // cause problems when running under a double-byte ccsid where // weird characters would not translate correctly. // Instead, we use the messageTextLength field to determine how // long the messageText is and translate it ourselves using a // CharConverter object. byte[] arr = (byte[])feedbackData[i]; //@B0A String text = (new CharConverter(system_.getCcsid())).byteArrayToString(arr, 112 + substitutionDataLength, textLength); //@B0A //@C0C //@B0D String text = //@B0D ((String) msg.getField("messageText")).substring(0, textLength); // Construct an AS400Message. msgs[i] = new AS400Message(id, text); msgs[i].setSeverity(severity); msgs[i].setType(type); } } return msgs; } /** *Positions the file cursor to after the last record. *@exception AS400Exception If the server returns an error message. *@exception AS400SecurityException If a security or authority error occurs. *@exception InterruptedException If this thread is interrupted. *@exception IOException If an error occurs while communicating with the server. **/ public void positionCursorAfterLast() throws AS400Exception, AS400SecurityException, InterruptedException, IOException { // If we are caching records and the cache contains the last record, // position the cache. Otherwise, position the file and refresh the // cache if we are caching records. if (cacheRecords_ && cache_.containsLastRecord()) { cache_.setPositionAfterLast(); return; } if (cacheRecords_) { // Invalidate the cache cache_.setIsEmpty(); } // Use force end of data. byte[] optl = { TYPE_GET_LAST, SHR_READ_NORM_RLS, DATA_NODTA_DTARCD, OPER_GET }; byte[] swapToPH = new byte[12]; byte[] swapFromPH = new byte[12]; boolean didSwap = system_.swapTo(swapToPH, swapFromPH); try { forceEndOfData(handle_, optl); } catch(NativeException e) { // Parse the message feedback data and throw AS400Exception throw new AS400Exception(parseMsgFeedback(e.data)); } finally { if (didSwap) system_.swapBack(swapToPH, swapFromPH); } } /** *Positions the file cursor to the specified position (first, last, next, *previous). *@param type the type of position operation *@exception AS400Exception If the server returns an error message. *@exception AS400SecurityException If a security or authority error occurs. *@exception InterruptedException If this thread is interrupted. *@exception IOException If an error occurs while communicating with the server. **/ public Record[] positionCursorAt(int type) throws AS400Exception, AS400SecurityException, InterruptedException, IOException { // Use GET to position the cursor. // byte shr = (as400File_.openType_ == AS400File.READ_ONLY ? // @A2D // SHR_READ_NORM : SHR_UPD_NORM); // @A2D // Start of @A2A byte shr; if ((openType_ == AS400File.READ_ONLY) || //@C0C ((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) //@C0C { // Read only shr = SHR_READ_NORM; } else { // READ_WRITE; get for update shr = SHR_UPD_NORM; } // End of @A2A byte[] optl = { (byte) type, shr, DATA_DTA_DTARCD, OPER_GET }; byte[] ctll = new byte[6]; ctll[0] = 0x10; // ID for number of records // Length of value field BinaryConverter.shortToByteArray((short) 2, ctll, 1); // Number of records to get BinaryConverter.shortToByteArray((short) blockingFactor_, ctll, 3); //@C0C ctll[5] = (byte)0xFF; // End of control list BytesWithOffset data = null; byte[] swapToPH = new byte[12]; byte[] swapFromPH = new byte[12]; boolean didSwap = system_.swapTo(swapToPH, swapFromPH); try { data = new BytesWithOffset(getForPosition(handle_, optl, ctll, blockingFactor_ * //@C0C openFeedback_.getRecordIncrement())); } catch(NativeException e) { // Parse the message feedback data and throw AS400Exception throw new AS400Exception(parseMsgFeedback(e.data)); } finally { if (didSwap) system_.swapBack(swapToPH, swapFromPH); } // Parse the I/O feedback data. LocalIOFB iofb = new LocalIOFB(data.data_, data.offset_); data.offset_ += 14; // Parse the record data. Record[] records = parseRecordData(iofb, data); return records; } //@RBA public Record[] positionCursorAtLong(int type) throws AS400Exception, AS400SecurityException, InterruptedException, IOException { return positionCursorAt(type); } /** *Positions the file cursor to before the first record. *@exception AS400Exception If the server returns an error message. *@exception AS400SecurityException If a security or authority error occurs. *@exception InterruptedException If this thread is interrupted. *@exception IOException If an error occurs while communicating with the server. **/ public void positionCursorBeforeFirst() throws AS400Exception, AS400SecurityException, InterruptedException, IOException { // If we are caching records and the cache contains the first record, // position the cache. Otherwise, position the file and refresh the // cache if we are caching records. if (cacheRecords_ && cache_.containsFirstRecord()) { cache_.setPositionBeforeFirst(); return; } if (cacheRecords_) { // Invalidate the cache cache_.setIsEmpty(); } // Use Force end of data to set the cursor. byte[] optl = { TYPE_GET_FIRST, SHR_READ_NORM_RLS, DATA_NODTA_DTARCD, OPER_GET }; byte[] swapToPH = new byte[12]; byte[] swapFromPH = new byte[12]; boolean didSwap = system_.swapTo(swapToPH, swapFromPH); try { forceEndOfData(handle_, optl); } catch(NativeException e) { // Parse the message feedback data and throw AS400Exception throw new AS400Exception(parseMsgFeedback(e.data)); } finally { if (didSwap) system_.swapBack(swapToPH, swapFromPH); } } /** *Positions the cursor to the record at the specified file position. *@parm index the file position *@exception AS400Exception If the server returns an error message. *@exception AS400SecurityException If a security or authority error occurs. *@exception InterruptedException If this thread is interrupted. *@exception IOException If an error occurs while communicating with the server. **/ public Record positionCursorToIndex(int index) throws AS400Exception, AS400SecurityException, InterruptedException, IOException { if (cacheRecords_ && !cache_.setPosition(index)) //@G0A { // Invalidate the cache cache_.setIsEmpty(); //@G0A } // OPTL // byte share = (as400File_.openType_ == AS400File.READ_ONLY ? // @A2D // SHR_READ_NORM : SHR_UPD_NORM); // @A2D // Start of @A2A byte share; if ((openType_ == AS400File.READ_ONLY) || //@C0C ((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) //@C0C { // Read only share = SHR_READ_NORM; } else { // READ_WRITE; get for update share = SHR_UPD_NORM; } // End of @A2A byte[] optl = { TYPE_GETD_ABSRRN, share, DATA_DTA_DTARCD, OPER_GETD }; // CTLL // ---------------------------------- // Offset Description // ---------------------------------- // 0 record format ID // 1 record format length // 3 record format name // 13 member number ID // 14 member number length // 16 member number value // 18 relative record number ID // 19 relative record number length // 21 relative record number // 25 control list end byte[] ctll = new byte[26]; ctll[0] = 1; BinaryConverter.shortToByteArray((short) 35, ctll, 1); // Start of @A2D /* StringBuffer recordName = new StringBuffer(as400File_.recordFormat_.getName()); while (recordName.length() < 10) recordName.append(' '); converter_.stringToByteArray(recordName.toString(), ctll, 3); */ // End of @A2D System.arraycopy(recordFormatCTLLName_, 0, ctll, 3, recordFormatCTLLName_.length); // @A2A //@C0C ctll[13] = 0xf; BinaryConverter.shortToByteArray((short) 2, ctll, 14); BinaryConverter.shortToByteArray((short) 0, ctll, 16); ctll[18] = 2; BinaryConverter.shortToByteArray((short) 4, ctll, 19); BinaryConverter.intToByteArray(index, ctll, 21); ctll[25] = (byte) 0xff; BytesWithOffset data = null; byte[] swapToPH = new byte[12]; byte[] swapFromPH = new byte[12]; boolean didSwap = system_.swapTo(swapToPH, swapFromPH); try { // GETD. data = new BytesWithOffset(getdForPosition(handle_, optl, ctll, openFeedback_.getRecordIncrement() * blockingFactor_)); //@C0C } catch(NativeException e) { // Parse the message feedback data and throw AS400Exception throw new AS400Exception(parseMsgFeedback(e.data)); } finally { if (didSwap) system_.swapBack(swapToPH, swapFromPH); } // Parse the I/O feedback data. LocalIOFB iofb = new LocalIOFB(data.data_, data.offset_); data.offset_ += 14; // Parse the record data. Record[] records = parseRecordData(iofb, data); return (records.length == 0)? null : records[0]; } //@RBA public Record positionCursorToIndexLong(long index) throws AS400Exception, AS400SecurityException, InterruptedException, IOException {return positionCursorToIndex((int)index);} /** *Positions the cursor to the first record in the file that matches the *specified keys. *@param keys the keys *@param searchType the way to compare keys *@exception AS400Exception If the server returns an error message. *@exception AS400SecurityException If a security or authority error occurs. *@exception InterruptedException If this thread is interrupted. *@exception IOException If an error occurs while communicating with the server. **/ public Record positionCursorToKey(Object[] keys, int searchType) throws AS400Exception, AS400SecurityException, InterruptedException, IOException { // OPTL // byte share = (as400File_.openType_ == AS400File.READ_ONLY ? // @A2D // SHR_READ_NORM : SHR_UPD_NORM); // @A2D if (cacheRecords_) //@G0A { // Invalidate the cache cache_.setIsEmpty(); //@G0A } // Start of @A2A byte share; if ((openType_ == AS400File.READ_ONLY) || //@C0C ((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) //@C0C { // Read only share = SHR_READ_NORM; } else { // READ_WRITE; get for update share = SHR_UPD_NORM; } // End of @A2A byte[] optl = { (byte)searchType, share, DATA_DTA_DTARCD, OPER_GETK }; // Determine the total length of all data in keyFields. FieldDescription description; ByteArrayOutputStream keyAsBytes = new ByteArrayOutputStream(); // Converter conv; // @A1D byte[] fieldAsBytes; // Will contain each key field's data as bytes byte[] lengthBytes = new byte[2]; // Used for variable length fields for (int i = 0; i < keys.length; i++) { try { // Convert each key field to server data writing it to keyAsBytes description = recordFormat_.getKeyFieldDescription(i); //@C0C // Check if field is a variable length field. This means that the field // is either a hex field or a character (DNCS* included) field. // The field has the potential of being variable length, but may be // fixed length. We account for both cases in this if. if (description instanceof VariableLengthFieldDescription) { boolean varLength = ((VariableLengthFieldDescription)description).isVariableLength(); if (description instanceof HexFieldDescription) { // Hex field if (varLength) { // Need to write two bytes of length info prior to writing the data BinaryConverter.shortToByteArray((short)((byte[])keys[i]).length, lengthBytes, 0); keyAsBytes.write(lengthBytes, 0, lengthBytes.length); } keyAsBytes.write((byte[])keys[i], 0, ((byte[])keys[i]).length); if (varLength) { // We need to send the maximum field length number of bytes for the // field, even though only keyFields[i].length bytes of the data will // be looked at by DDM int fieldLength = description.getDataType().getByteLength(); for (int j = ((byte[])keys[i]).length; j < fieldLength; ++j) { keyAsBytes.write(0); } } } else { // Character field // Use Converter object to translate the key field passed in. If // we use the data type associated with the field description, the data // for the key field will be padded with blanks to the byteLength of // the field description. This can cause a match of the key to occur in the // case that the user specifies a value that does not exactly match the key // field in the record. if (varLength) { // Need to write two bytes of length info prior to writing the // data BinaryConverter.shortToByteArray((short)((String)keys[i]).length(), lengthBytes, 0); keyAsBytes.write(lengthBytes, 0, lengthBytes.length); } // @A1D // Modified code to use the AS400Text object to do the conversion. // conv = Converter.getConverter(((AS400Text)description.getDataType()).getCcsid()); // @A1D // fieldAsBytes = conv.stringToByteArray((String)keys[i]); // @A1D fieldAsBytes = description.getDataType().toBytes(keys[i]); // @A1A keyAsBytes.write(fieldAsBytes, 0, fieldAsBytes.length); // We need to get rid of this now since AS400Text does the padding for us. // @A1D /* if (varLength) { // We need to send the maximum field length number of bytes for the // field, even though only keyFields[i].length bytes of the data will // be looked at by DDM int fieldLength = description.getDataType().getByteLength(); for (int j = ((String)keys[i]).length(); j < fieldLength; ++j) { keyAsBytes.write(0x40); } } */ } } else { // Numeric field fieldAsBytes = description.getDataType().toBytes(keys[i]); keyAsBytes.write(fieldAsBytes, 0, fieldAsBytes.length); } } catch(NullPointerException e) { // One of the key fields was null throw new ExtendedIllegalArgumentException("key", ExtendedIllegalArgumentException.PARAMETER_VALUE_NOT_VALID); } } int keyLength = keyAsBytes.size(); // CTLL // ---------------------------------- // Offset Description // ---------------------------------- // 0 record format ID // 1 record format length // 3 record format name // 13 member number ID // 14 member number length // 16 member number value // 18 number of fields ID // 19 number of fields length // 21 number of fields value // 25 key fields ID // 26 key fields length, // 28 key field values // ?? control list end byte[] ctll = new byte[29 + keyLength]; ctll[0] = 1; BinaryConverter.shortToByteArray((short) 35, ctll, 1); // Start of @A2D /* StringBuffer recordName = new StringBuffer(as400File_.recordFormat_.getName()); while (recordName.length() < 10) recordName.append(' '); converter_.stringToByteArray(recordName.toString(), ctll, 3); */ // End of @A2D System.arraycopy(recordFormatCTLLName_, 0, ctll, 3, recordFormatCTLLName_.length); // @A2A //@C0C ctll[13] = 0xf; BinaryConverter.shortToByteArray((short) 2, ctll, 14); BinaryConverter.shortToByteArray((short) 0, ctll, 16); ctll[18] = 8; BinaryConverter.shortToByteArray((short) 4, ctll, 19); BinaryConverter.intToByteArray(keys.length, ctll, 21); ctll[25] = 7; BinaryConverter.shortToByteArray((short) keyLength, ctll, 26); System.arraycopy(keyAsBytes.toByteArray(), 0, ctll, 28, keyLength); ctll[28 + keyLength] = (byte) 0xff; BytesWithOffset data = null; byte[] swapToPH = new byte[12]; byte[] swapFromPH = new byte[12]; boolean didSwap = system_.swapTo(swapToPH, swapFromPH); try { // GETK data = new BytesWithOffset(getkForPosition(handle_, optl, ctll, openFeedback_.getRecordIncrement() * blockingFactor_)); //@C0C } catch(NativeException e) { // Parse the message feedback data and throw AS400Exception throw new AS400Exception(parseMsgFeedback(e.data)); } finally { if (didSwap) system_.swapBack(swapToPH, swapFromPH); } // Parse the I/O feedback data. // LocalIOFB iofb = new LocalIOFB(data.data_, data.offset_); // @A2D // data.offset_ += 14; // @A2D // Parse the record data. // Record[] records = parseRecordData(iofb, data); // @A2D // return (records.length == 0)? null : records[0]; // @A2D return null; } //@RBA public Record positionCursorToKeyLong(Object[] keys, int searchType) throws AS400Exception, AS400SecurityException, InterruptedException, IOException { return positionCursorToKey(keys,searchType); } // @A2A /** *Positions the cursor to the first record in the file that matches the *specified keys. *@param keys the keys *@param searchType the way to compare keys *@exception AS400Exception If the server returns an error message. *@exception AS400SecurityException If a security or authority error occurs. *@exception InterruptedException If this thread is interrupted. *@exception IOException If an error occurs while communicating with the server. **/ public Record positionCursorToKey(byte[] keys, int searchType, int numberOfKeyFields) throws AS400Exception, AS400SecurityException, InterruptedException, IOException { // OPTL // byte share = (as400File_.openType_ == AS400File.READ_ONLY ? // SHR_READ_NORM : SHR_UPD_NORM); if (cacheRecords_) //@G0A { // Invalidate the cache cache_.setIsEmpty(); //@G0A } // Start of @A2A byte share; if ((openType_ == AS400File.READ_ONLY) || //@C0C ((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) //@C0C { // Read only share = SHR_READ_NORM; } else { // READ_WRITE; get for update share = SHR_UPD_NORM; } // End of @A2A byte[] optl = { (byte)searchType, share, DATA_DTA_DTARCD, OPER_GETK }; // Determine the total length of all data in keyFields. FieldDescription description; int keyLength = keys.length; // CTLL // ---------------------------------- // Offset Description // ---------------------------------- // 0 record format ID // 1 record format length // 3 record format name // 13 member number ID // 14 member number length // 16 member number value // 18 number of fields ID // 19 number of fields length // 21 number of fields value // 25 key fields ID // 26 key fields length, // 28 key field values // ?? control list end byte[] ctll = new byte[29 + keyLength]; ctll[0] = 1; BinaryConverter.shortToByteArray((short) 35, ctll, 1); // Start of @A2D /* StringBuffer recordName = new StringBuffer(as400File_.recordFormat_.getName()); while (recordName.length() < 10) recordName.append(' '); converter_.stringToByteArray(recordName.toString(), ctll, 3); */ // End of @A2D System.arraycopy(recordFormatCTLLName_, 0, ctll, 3, recordFormatCTLLName_.length); // @A2A //@C0C ctll[13] = 0xf; BinaryConverter.shortToByteArray((short) 2, ctll, 14); BinaryConverter.shortToByteArray((short) 0, ctll, 16); ctll[18] = 8; BinaryConverter.shortToByteArray((short) 4, ctll, 19); BinaryConverter.intToByteArray(numberOfKeyFields, ctll, 21); ctll[25] = 7; BinaryConverter.shortToByteArray((short) keyLength, ctll, 26); System.arraycopy(keys, 0, ctll, 28, keyLength); ctll[28 + keyLength] = (byte) 0xff; BytesWithOffset data = null; byte[] swapToPH = new byte[12]; byte[] swapFromPH = new byte[12]; boolean didSwap = system_.swapTo(swapToPH, swapFromPH); try { // GETK data = new BytesWithOffset(getkForPosition(handle_, optl, ctll, openFeedback_.getRecordIncrement() * blockingFactor_)); //@C0C } catch(NativeException e) { // Parse the message feedback data and throw AS400Exception throw new AS400Exception(parseMsgFeedback(e.data)); } finally { if (didSwap) system_.swapBack(swapToPH, swapFromPH); } // Parse the I/O feedback data. // LocalIOFB iofb = new LocalIOFB(data.data_, data.offset_); // @A2D // data.offset_ += 14; // @A2D // Parse the record data. // Record[] records = parseRecordData(iofb, data); // @A2D // return (records.length == 0)? null : records[0]; // @A2D return null; } //@RBA public Record positionCursorToKeyLong(byte[] keys, int searchType, int numberOfKeyFields) throws AS400Exception, AS400SecurityException, InterruptedException, IOException {return positionCursorToKey(keys,searchType,numberOfKeyFields);} /** *Write records to the file. *@param handle the file handle. *@param optl option list *@param ctll control list *@param data record data **/ native void put(int handle, byte[] optl, byte[] ctll, byte[] data) throws NativeException; /** Reads the record at the specified file position. @param index the file position @return the record read. *@exception AS400Exception If the server returns an error message. *@exception AS400SecurityException If a security or authority error occurs. *@exception InterruptedException If this thread is interrupted. *@exception IOException If an error occurs while communicating with the server. **/ public Record read(int index) throws AS400Exception, AS400SecurityException, InterruptedException, IOException { if (cacheRecords_) //@C0A return super.read(index); //@C0A // OPTL // byte share = (as400File_.openType_ == AS400File.READ_ONLY ? // @A2D // SHR_READ_NORM : SHR_UPD_NORM); // @A2D // Start of @A2A byte share; if ((openType_ == AS400File.READ_ONLY) || //@C0C ((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) //@C0C { // Read only share = SHR_READ_NORM; } else { // READ_WRITE; get for update share = SHR_UPD_NORM; } // End of @A2A byte[] optl = { TYPE_GETD_ABSRRN, share, DATA_DTA_DTARCD, OPER_GETD }; // CTLL // ---------------------------------- // Offset Description // ---------------------------------- // 0 record format ID // 1 record format length // 3 record format name // 13 member number ID // 14 member number length // 16 member number value // 18 relative record number ID // 19 relative record number length // 21 relative record number // 25 control list end byte[] ctll = new byte[26]; ctll[0] = 1; BinaryConverter.shortToByteArray((short) 35, ctll, 1); // Start of @A2D /* StringBuffer recordName = new StringBuffer(as400File_.recordFormat_.getName()); while (recordName.length() < 10) recordName.append(' '); converter_.stringToByteArray(recordName.toString(), ctll, 3); */ // End of @A2D System.arraycopy(recordFormatCTLLName_, 0, ctll, 3, recordFormatCTLLName_.length); // @A2A //@C0C ctll[13] = 0xf; BinaryConverter.shortToByteArray((short) 2, ctll, 14); BinaryConverter.shortToByteArray((short) 0, ctll, 16); ctll[18] = 2; BinaryConverter.shortToByteArray((short) 4, ctll, 19); BinaryConverter.intToByteArray(index, ctll, 21); ctll[25] = (byte) 0xff; BytesWithOffset data = null; byte[] swapToPH = new byte[12]; byte[] swapFromPH = new byte[12]; boolean didSwap = system_.swapTo(swapToPH, swapFromPH); try { // GETD data = new BytesWithOffset(getdForRead(handle_, optl, ctll, openFeedback_.getRecordIncrement() * blockingFactor_)); //@C0C } catch(NativeException e) { // Parse the message feedback data and throw AS400Exception throw new AS400Exception(parseMsgFeedback(e.data)); } finally { if (didSwap) system_.swapBack(swapToPH, swapFromPH); } // Parse the I/O feedback data. LocalIOFB iofb = new LocalIOFB(data.data_, data.offset_); data.offset_ += 14; // Parse the record data. Record[] records = parseRecordData(iofb, data); return (records.length == 0)? null : records[0]; } /** *Reads the first record with the specified key based on the specified search type. *@param key The values that make up the key with which to find the record. *@param searchType The type of read. This value is one of the TYPE_GETKEY_* constants. *@return The record read. *@exception AS400Exception If the server returns an error message. *@exception AS400SecurityException If a security or authority error occurs. *@exception InterruptedException If this thread is interrupted. *@exception IOException If an error occurs while communicating with the server. **/ public Record read(Object[] key, int searchType) throws AS400Exception, AS400SecurityException, InterruptedException, IOException { // OPTL // byte share = (as400File_.openType_ == AS400File.READ_ONLY ? // @A2D // SHR_READ_NORM : SHR_UPD_NORM); // @A2D // Start of @A2A byte share; if ((openType_ == AS400File.READ_ONLY) || //@C0C ((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) //@C0C { // Read only share = SHR_READ_NORM; } else { // READ_WRITE; get for update share = SHR_UPD_NORM; } // End of @A2A byte[] optl = { (byte)searchType, share, DATA_DTA_DTARCD, OPER_GETK }; // Determine the total length of all data in keyFields. FieldDescription description; ByteArrayOutputStream keyAsBytes = new ByteArrayOutputStream(); // Converter conv; // @A1D byte[] fieldAsBytes; // Will contain each key field's data as bytes byte[] lengthBytes = new byte[2]; // Used for variable length fields for (int i = 0; i < key.length; i++) { try { // Convert each key field to server data writing it to keyAsBytes description = recordFormat_.getKeyFieldDescription(i); //@C0C // Check if field is a variable length field. This means that the field // is either a hex field or a character (DNCS* included) field. // The field has the potential of being variable length, but may be // fixed length. We account for both cases in this if. if (description instanceof VariableLengthFieldDescription) { boolean varLength = ((VariableLengthFieldDescription)description).isVariableLength(); if (description instanceof HexFieldDescription) { // Hex field if (varLength) { // Need to write two bytes of length info prior to writing the data BinaryConverter.shortToByteArray((short)((byte[])key[i]).length, lengthBytes, 0); keyAsBytes.write(lengthBytes, 0, lengthBytes.length); } keyAsBytes.write((byte[])key[i], 0, ((byte[])key[i]).length); if (varLength) { // We need to send the maximum field length number of bytes for the // field, even though only keyFields[i].length bytes of the data will // be looked at by DDM int fieldLength = description.getDataType().getByteLength(); for (int j = ((byte[])key[i]).length; j < fieldLength; ++j) { keyAsBytes.write(0); } } } else { // Character field // Use Converter object to translate the key field passed in. If // we use the data type associated with the field description, the data // for the key field will be padded with blanks to the byteLength of // the field description. This can cause a match of the key to occur in the // case that the user specifies a value that does not exactly match the key // field in the record. if (varLength) { // Need to write two bytes of length info prior to writing the // data BinaryConverter.shortToByteArray((short)((String)key[i]).length(), lengthBytes, 0); keyAsBytes.write(lengthBytes, 0, lengthBytes.length); } // conv = Converter.getConverter(((AS400Text)description.getDataType()).getCcsid()); // @A1D // fieldAsBytes = conv.stringToByteArray((String)key[i]); // @A1D fieldAsBytes = description.getDataType().toBytes(key[i]); // @A1A keyAsBytes.write(fieldAsBytes, 0, fieldAsBytes.length); // @A1D /* if (varLength) { // We need to send the maximum field length number of bytes for the // field, even though only keyFields[i].length bytes of the data will // be looked at by DDM int fieldLength = description.getDataType().getByteLength(); for (int j = ((String)key[i]).length(); j < fieldLength; ++j) { keyAsBytes.write(0x40); } } */ } } else { // Numeric field fieldAsBytes = description.getDataType().toBytes(key[i]); keyAsBytes.write(fieldAsBytes, 0, fieldAsBytes.length); } } catch(NullPointerException e) { // One of the key fields was null throw new ExtendedIllegalArgumentException("key", ExtendedIllegalArgumentException.PARAMETER_VALUE_NOT_VALID); } } int keyLength = keyAsBytes.size(); // CTLL // ---------------------------------- // Offset Description // ---------------------------------- // 0 record format ID // 1 record format length // 3 record format name // 13 member number ID // 14 member number length // 16 member number value // 18 number of fields ID // 19 number of fields length // 21 number of fields value // 25 key fields ID // 26 key fields length, // 28 key field values // ?? control list end byte[] ctll = new byte[29 + keyLength]; ctll[0] = 1; BinaryConverter.shortToByteArray((short) 35, ctll, 1); // Start of @A2D /* StringBuffer recordName = new StringBuffer(as400File_.recordFormat_.getName()); while (recordName.length() < 10) recordName.append(' '); converter_.stringToByteArray(recordName.toString(), ctll, 3); */ // End of @A2D System.arraycopy(recordFormatCTLLName_, 0, ctll, 3, recordFormatCTLLName_.length); // @A2A //@C0C ctll[13] = 0xf; BinaryConverter.shortToByteArray((short) 2, ctll, 14); BinaryConverter.shortToByteArray((short) 0, ctll, 16); ctll[18] = 8; BinaryConverter.shortToByteArray((short) 4, ctll, 19); BinaryConverter.intToByteArray(key.length, ctll, 21); ctll[25] = 7; BinaryConverter.shortToByteArray((short) keyLength, ctll, 26); System.arraycopy(keyAsBytes.toByteArray(), 0, ctll, 28, keyLength); ctll[28 + keyLength] = (byte) 0xff; BytesWithOffset data = null; byte[] swapToPH = new byte[12]; byte[] swapFromPH = new byte[12]; boolean didSwap = system_.swapTo(swapToPH, swapFromPH); try { // GETK. data = new BytesWithOffset(getkForRead(handle_, optl, ctll, openFeedback_.getRecordIncrement() * blockingFactor_)); //@C0C } catch(NativeException e) { // Parse the message feedback data and throw AS400Exception throw new AS400Exception(parseMsgFeedback(e.data)); } finally { if (didSwap) system_.swapBack(swapToPH, swapFromPH); } // Parse the I/O feedback data. LocalIOFB iofb = new LocalIOFB(data.data_, data.offset_); data.offset_ += 14; // Parse the record data. Record[] records = parseRecordData(iofb, data); if (cacheRecords_) //@C0A { //@C0A cache_.setIsEmpty(); //@C0A } //@C0A return (records.length == 0)? null : records[0]; } //@RBA public Record readLong(Object[] key, int searchType) throws AS400Exception, AS400SecurityException, InterruptedException, IOException { return null; } // @A2A /** *Reads the first record with the specified key based on the specified search type. *@param key The values that make up the key with which to find the record. *@param searchType The type of read. This value is one of the TYPE_GETKEY_* constants. *@return The record read. *@exception AS400Exception If the server returns an error message. *@exception AS400SecurityException If a security or authority error occurs. *@exception InterruptedException If this thread is interrupted. *@exception IOException If an error occurs while communicating with the server. **/ public Record read(byte[] key, int searchType, int numberOfKeyFields) throws AS400Exception, AS400SecurityException, InterruptedException, IOException { // OPTL // byte share = (as400File_.openType_ == AS400File.READ_ONLY ? // SHR_READ_NORM : SHR_UPD_NORM); // Start of @A2A byte share; if ((openType_ == AS400File.READ_ONLY) || //@C0C ((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) //@C0C { // Read only share = SHR_READ_NORM; } else { // READ_WRITE; get for update share = SHR_UPD_NORM; } // End of @A2A byte[] optl = { (byte)searchType, share, DATA_DTA_DTARCD, OPER_GETK }; // Determine the total length of all data in keyFields. FieldDescription description; int keyLength = key.length; // CTLL // ---------------------------------- // Offset Description // ---------------------------------- // 0 record format ID // 1 record format length // 3 record format name // 13 member number ID // 14 member number length // 16 member number value // 18 number of fields ID // 19 number of fields length // 21 number of fields value // 25 key fields ID // 26 key fields length, // 28 key field values // ?? control list end byte[] ctll = new byte[29 + keyLength]; ctll[0] = 1; BinaryConverter.shortToByteArray((short) 35, ctll, 1); // Start of @A2D /* StringBuffer recordName = new StringBuffer(as400File_.recordFormat_.getName()); while (recordName.length() < 10) recordName.append(' '); converter_.stringToByteArray(recordName.toString(), ctll, 3); */ // Start of @A2D System.arraycopy(recordFormatCTLLName_, 0, ctll, 3, recordFormatCTLLName_.length); // @A2A //@C0C ctll[13] = 0xf; BinaryConverter.shortToByteArray((short) 2, ctll, 14); BinaryConverter.shortToByteArray((short) 0, ctll, 16); ctll[18] = 8; BinaryConverter.shortToByteArray((short) 4, ctll, 19); BinaryConverter.intToByteArray(numberOfKeyFields, ctll, 21); ctll[25] = 7; BinaryConverter.shortToByteArray((short) keyLength, ctll, 26); System.arraycopy(key, 0, ctll, 28, keyLength); ctll[28 + keyLength] = (byte) 0xff; BytesWithOffset data = null; byte[] swapToPH = new byte[12]; byte[] swapFromPH = new byte[12]; boolean didSwap = system_.swapTo(swapToPH, swapFromPH); try { // GETK. data = new BytesWithOffset(getkForRead(handle_, optl, ctll, openFeedback_.getRecordIncrement() * blockingFactor_)); //@C0C } catch(NativeException e) { // Parse the message feedback data and throw AS400Exception throw new AS400Exception(parseMsgFeedback(e.data)); } finally { if (didSwap) system_.swapBack(swapToPH, swapFromPH); } // Parse the I/O feedback data. LocalIOFB iofb = new LocalIOFB(data.data_, data.offset_); data.offset_ += 14; // Parse the record data. Record[] records = parseRecordData(iofb, data); if (cacheRecords_) //@C0A { //@C0A cache_.setIsEmpty(); //@C0A } //@C0A return (records.length == 0)? null : records[0]; } public Record readLong(byte[] key, int searchType, int numberOfKeyFields) throws AS400Exception, AS400SecurityException, InterruptedException, IOException { return read(key,searchType,numberOfKeyFields); } /** *Reads all the records in the file. *@param fileType The type of file. Valid values are: key or seq *@return The records read. *@exception AS400Exception If the server returns an error message. *@exception AS400SecurityException If a security or authority error occurs. *@exception InterruptedException If this thread is interrupted. *@exception IOException If an error occurs while communicating with the server. **/ public Record[] readAll(String fileType, int bf) //@C1C throws AS400Exception, AS400SecurityException, InterruptedException, IOException { // Open the file with a blocking factor of 100. openFile2(AS400File.READ_ONLY, bf, AS400File.COMMIT_LOCK_LEVEL_NONE, fileType); //@C0C @C1C Record[] records = null; try { // Determine how many records are in the file. records = new Record[openFeedback_.getNumberOfRecords()]; // Read the entire file contents one record at a time. for (int i = 0; i < records.length; i++) { records[i] = readNext(); //@C0C } } finally { // Close the file we opened. try { close(); //@C0C } catch(AS400Exception e) { resetState(); //@C0C throw e; } catch(AS400SecurityException e) { resetState(); //@C0C throw e; } catch(InterruptedException e) { resetState(); //@C0C throw e; } catch(IOException e) { resetState(); //@C0C throw e; } } return records; } //@RBA public Record[] readAllLong(String fileType, int bf) //@C1C throws AS400Exception, AS400SecurityException, InterruptedException, IOException { return readAll( fileType, bf); } //@RBA public Record readRecordLong(int type) throws AS400Exception, AS400SecurityException, InterruptedException, IOException {return readRecord(type);} /** *Reads the record at the current file position. *@param type type of read (first, last, next, previous) *@return the record read. *@exception AS400Exception If the server returns an error message. *@exception AS400SecurityException If a security or authority error occurs. *@exception InterruptedException If this thread is interrupted. *@exception IOException If an error occurs while communicating with the server. **/ public Record readRecord(int type) throws AS400Exception, AS400SecurityException, InterruptedException, IOException { // OPTL // byte share = (as400File_.openType_ == AS400File.READ_ONLY ? // @A2D // SHR_READ_NORM : SHR_UPD_NORM); // @A2D // Start of @A2A byte share; if ((openType_ == AS400File.READ_ONLY) || //@C0C ((openType_ == AS400File.READ_WRITE) && readNoUpdate_)) //@C0C { // Read only share = SHR_READ_NORM; } else { // READ_WRITE; get for update share = SHR_UPD_NORM; } // End of @A2A byte[] optl = { (byte) type, share, DATA_DTA_DTARCD, OPER_GET }; byte[] ctll = new byte[6]; ctll[0] = 0x10; // ID for number of records // Length of value field BinaryConverter.shortToByteArray((short) 2, ctll, 1); // Number of records to get BinaryConverter.shortToByteArray((short) blockingFactor_, ctll, 3); //@C0C ctll[5] = (byte)0xFF; // End of control list BytesWithOffset data = null; byte[] swapToPH = new byte[12]; byte[] swapFromPH = new byte[12]; boolean didSwap = system_.swapTo(swapToPH, swapFromPH); try { // GET data = new BytesWithOffset(getForRead(handle_, optl, ctll, openFeedback_.getRecordIncrement() * blockingFactor_)); //@C0C } catch(NativeException e) { // Parse the message feedback data and throw AS400Exception throw new AS400Exception(parseMsgFeedback(e.data)); } finally { if (didSwap) system_.swapBack(swapToPH, swapFromPH); } // Parse the I/O feedback data. LocalIOFB iofb = new LocalIOFB(data.data_, data.offset_); data.offset_ += 14; // Parse the record data. Record[] records = parseRecordData(iofb, data); return (records.length == 0)? null : records[0]; } //@RBA public Record[] readRecordsLong(int direction) throws AS400Exception, AS400SecurityException, InterruptedException, IOException { return null; } /** *Reads records from the file. The next or previous 'blockingFactor_' *records are retrieved depending on the direction specified. *@param direction (DDMRecordCache.FORWARD or DDMRecordCache.BACKWARD) *@return the records read *@exception AS400Exception If the server returns an error message. *@exception AS400SecurityException If a security or authority error occurs. *@exception InterruptedException If this thread is interrupted. *@exception IOException If an error occurs while communicating with the server. **/ public Record[] readRecords(int direction) throws AS400Exception, AS400SecurityException, InterruptedException, IOException { // OPTL byte type = (direction == DDMRecordCache.FORWARD ? TYPE_GET_NEXT : TYPE_GET_PREV); byte[] optl = { type, SHR_READ_NORM, DATA_DTA_DTARCD, OPER_GET }; byte[] ctll = new byte[6]; ctll[0] = 0x10; // ID for number of records // Length of value field BinaryConverter.shortToByteArray((short) 2, ctll, 1); // Number of records to get BinaryConverter.shortToByteArray((short) blockingFactor_, ctll, 3); //@C0C ctll[5] = (byte)0xFF; // End of control list // GET. int length = openFeedback_.getRecordIncrement() * blockingFactor_; //@C0C BytesWithOffset data = null; byte[] swapToPH = new byte[12]; byte[] swapFromPH = new byte[12]; boolean didSwap = system_.swapTo(swapToPH, swapFromPH); try { data = new BytesWithOffset(getForRead(handle_, optl, ctll, length)); } catch(NativeException e) { // Parse the message feedback data and throw AS400Exception throw new AS400Exception(parseMsgFeedback(e.data)); } finally { if (didSwap) system_.swapBack(swapToPH, swapFromPH); } // Parse the I/O feedback data. LocalIOFB iofb = new LocalIOFB(data.data_, data.offset_); data.offset_ += 14; // Parse the record data. Record[] records = parseRecordData(iofb, data); return records; } //@E3A /** * Used to reset the static hashtable of file handles that is * stored in the native code. We do this now because the * service program does not get reactivated when * another JVM is instantiated (as of the release after v4r5). **/ private static native void resetStaticStorage(); /** *Rolls back any transactions since the last commit/rollback boundary. Invoking this *method will cause all transactions under commitment control for this connection *to be rolled back. This means that any AS400File object for which a commit *lock level was specified and that was opened under this connection will have *outstanding transactions rolled back. *@exception AS400Exception If the server returns an error message. *@exception AS400SecurityException If a security or authority error occurs. *@exception InterruptedException If this thread is interrupted. *@exception IOException If an error occurs while communicating with the server. **/ public void rollback() throws AS400Exception, AS400SecurityException, InterruptedException, IOException { byte[] swapToPH = new byte[12]; byte[] swapFromPH = new byte[12]; boolean didSwap = system_.swapTo(swapToPH, swapFromPH); try { // Rollback. rollbackNtv(); } catch(NativeException e) { // Parse the message feedback data and throw AS400Exception throw new AS400Exception(parseMsgFeedback(e.data)); } finally { if (didSwap) system_.swapBack(swapToPH, swapFromPH); } } /** *Rollback changes to files under commitment control. **/ native void rollbackNtv() throws NativeException; /** *Updates the specified record. *@param handle the file handle *@param optl option list *@param ctll control list *@param recordData record data **/ native void updat(int handle, byte[] optl, byte[] ctll, byte[] recordData) throws NativeException; /** *Updates the record at the current cursor position. *@exception AS400Exception If the server returns an error message. *@exception AS400SecurityException If a security or authority error occurs. *@exception InterruptedException If this thread is interrupted. *@exception IOException If an error occurs while communicating with the server. **/ public void update(Record record) throws AS400Exception, AS400SecurityException, InterruptedException, IOException { if (record.getRecordLength() != openFeedback_.getRecordLength()) { if (Trace.isTraceOn() && Trace.isTraceErrorOn()) { Trace.log(Trace.ERROR, "Incorrect record length for file :"); Trace.log(Trace.ERROR, "record.getRecordLength() :" + String.valueOf(record.getRecordLength())); } throw new ExtendedIllegalArgumentException("record", ExtendedIllegalArgumentException. PARAMETER_VALUE_NOT_VALID); } // OPTL byte[] optl = { TYPE_GET_SAME, SHR_UPD_NORM, DATA_DTA_DTARCD, OPER_UPDATE }; // Create the record data to be sent int recordIncrement = openFeedback_.getRecordIncrement(); byte[] recordData = new byte[recordIncrement]; System.arraycopy(record.getContents(), 0, recordData, 0, record.getRecordLength()); // Start of changes for ----------------------------------------- @A3A // Determine the maxNumberOfFieldsPerFormatInFile for the file // remembering that multi-format logical files may have a different // number of getNumberOfFields() per record format. // DDMObjectDataStream.getObjectS38BUF() needs maxNumberOfFieldsPerFormatInFile int numFields = record.getNumberOfFields(); int maxNumberOfFieldsPerFormatInFile = numFields; if (rfCache_ == null) { // Use default maxNumberOfFieldsPerFormatInFile == numFields } else // Determine if a different record format has more fields (i.e. find the max) { int numberOfRecordFormats = rfCache_.length; for(int i = 0; i < numberOfRecordFormats; ++i) { maxNumberOfFieldsPerFormatInFile = Math.max(maxNumberOfFieldsPerFormatInFile, rfCache_[i].getNumberOfFields()); } } // End of changes for ------------------------------------------- @A3A int fieldOffset = recordIncrement - maxNumberOfFieldsPerFormatInFile; //@A3C for (int i = 0; i < numFields; ++i, ++fieldOffset) { recordData[fieldOffset] = (record.isNullField(i))? (byte)0xF1 : (byte)0xF0; } byte[] swapToPH = new byte[12]; byte[] swapFromPH = new byte[12]; boolean didSwap = system_.swapTo(swapToPH, swapFromPH); try { // UPDAT updat(handle_, optl, null, recordData); } catch(NativeException e) { // Parse the message feedback data and throw AS400Exception throw new AS400Exception(parseMsgFeedback(e.data)); } finally { if (didSwap) system_.swapBack(swapToPH, swapFromPH); } } /** *Writes an array of records to the end of the file. *The cursor is positioned to after the last record of the file as a result *of invoking this method. *@param records The records to write. The records must have a format *which matches the record format of this object. To ensure that this *requirement is met, use the *RecordFormat.getNewRecord() *method to obtain default records whose fields can be set appropriately by *the Java program and then written to the file. *@exception AS400Exception If the server returns an error message. *@exception AS400SecurityException If a security or authority error occurs. *@exception InterruptedException If this thread is interrupted. *@exception IOException If an error occurs while communicating with the server. **/ public void write(Record[] records) throws AS400Exception, AS400SecurityException, InterruptedException, IOException { if (records[0].getRecordLength() != openFeedback_.getRecordLength()) { if (Trace.isTraceOn() && Trace.isTraceErrorOn()) { Trace.log(Trace.ERROR, "Incorrect record length for file :"); Trace.log(Trace.ERROR, "record.getRecordLength() :" + String.valueOf(records[0].getRecordLength())); } throw new ExtendedIllegalArgumentException("records", ExtendedIllegalArgumentException. PARAMETER_VALUE_NOT_VALID); } // PUT the record data to the file, blocking factor records at a time. int recordIncrement = openFeedback_.getRecordIncrement(); byte[] recordData = new byte[(records.length < blockingFactor_ ? //@C0C records.length : blockingFactor_) * //@C0C recordIncrement]; // Start of changes for ----------------------------------------- @A3A // Determine the maxNumberOfFieldsPerFormatInFile for the file // remembering that multi-format logical files may have a different // number of getNumberOfFields() per record format. // DDMObjectDataStream.getObjectS38BUF() needs maxNumberOfFieldsPerFormatInFile int maxNumberOfFieldsPerFormatInFile = -1; if (rfCache_ == null) { // Use default maxNumberOfFieldsPerFormatInFile == -1 } else // Determine if a different record format has more fields (i.e. find the max) { int numberOfRecordFormats = rfCache_.length; for(int i = 0; i < numberOfRecordFormats; ++i) { maxNumberOfFieldsPerFormatInFile = Math.max(maxNumberOfFieldsPerFormatInFile, rfCache_[i].getNumberOfFields()); } } // End of changes for ------------------------------------------- @A3A for (int offset = 0, r = 1; r <= records.length; r++) { // Copy the record data to the record data buffer. byte[] record = records[r-1].getContents(); System.arraycopy(record, 0, recordData, offset, record.length); // Write the null field byte map array after the record data. It // immediately preceeds the next record. 0xf1 = null, 0xf0 != null // There may be a gap between the end of the record data and the // start of the null field byte map. int numFields = records[r-1].getNumberOfFields(); if (maxNumberOfFieldsPerFormatInFile == -1) //@A3A { //@A3A maxNumberOfFieldsPerFormatInFile = numFields; //@A3A } //@A3A for (int f = 0, fieldOffset = offset + //@A3C (recordIncrement - maxNumberOfFieldsPerFormatInFile); f < numFields; fieldOffset++, f++) { recordData[fieldOffset] = (records[r-1].isNullField(f) ? (byte) 0xf1 : (byte) 0xf0); } // If we've accumulated the data from blocking factor records or // have added data from the last record, PUT the record data. if (r == records.length && r > blockingFactor_ && r % blockingFactor_ != 0) //@C0C { byte[] dataToWrite = new byte[offset + recordIncrement]; System.arraycopy(recordData, 0, dataToWrite, 0, dataToWrite.length); byte[] swapToPH = new byte[12]; byte[] swapFromPH = new byte[12]; boolean didSwap = system_.swapTo(swapToPH, swapFromPH); try { // PUT put(handle_, null, null, dataToWrite); } catch(NativeException e) { // Parse the message feedback data and throw AS400Exception throw new AS400Exception(parseMsgFeedback(e.data)); } finally { if (didSwap) system_.swapBack(swapToPH, swapFromPH); } } else if (r == records.length || (r % blockingFactor_ == 0)) //@C0C { byte[] swapToPH = new byte[12]; byte[] swapFromPH = new byte[12]; boolean didSwap = system_.swapTo(swapToPH, swapFromPH); try { // PUT put(handle_, null, null, recordData); } catch(NativeException e) { // Parse the message feedback data and throw AS400Exception throw new AS400Exception(parseMsgFeedback(e.data)); } finally { if (didSwap) system_.swapBack(swapToPH, swapFromPH); } // Reset the offset. offset = 0; } else { // Increment the offset. offset += recordIncrement; } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy