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

com.ibm.as400.access.IFSFileDescriptorImplRemote Maven / Gradle / Ivy

The newest version!
///////////////////////////////////////////////////////////////////////////////
//                                                                             
// JTOpen (IBM Toolbox for Java - OSS version)                              
//                                                                             
// Filename: IFSFileDescriptorImplRemote.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-2000 International Business Machines Corporation and     
// others. All rights reserved.                                                
//                                                                             
///////////////////////////////////////////////////////////////////////////////
// @A3 - 10/18/2007 - Update finalize0() method to not send TWO close requests
//                    to the server.  When garbage collection finds a descriptor
//                    that has not been closed, it calls the finalize method on 
//                    one of the various IFSFilexxx classes which calls the 
//                    finalize0() method of this class.
//                    This class was sending TWO close requests to the server 
//                    for the same fileHandle_.  Normally, the second close would
//                    silently fail.  However, in some cases another thread might
//                    issue an open request for another file BETWEEN the two close
//                    requests sent by the garbage collection thread.  This 
//                    results in the newly opened file being incorrectly closed.
//                    Fix to finalize0() is to issue ONE close request to the 
//                    File server.
///////////////////////////////////////////////////////////////////////////////

package com.ibm.as400.access;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.Vector;


/**
 Provides a full remote implementation for the IFSFileDescriptor class.
 **/
class IFSFileDescriptorImplRemote implements IFSFileDescriptorImpl
{
    private static final int UNINITIALIZED = -1;
    private static final int MAX_BYTES_PER_READ = 16000000;  // limit of file server

    // Note: We allow direct access to some of these fields, for performance.
    ConverterImplRemote converter_;
    private int         fileHandle_ = UNINITIALIZED;
    int                 preferredServerCCSID_;
    private int         fileDataCCSID_ = UNINITIALIZED;
    private int         fileAsp_ = UNINITIALIZED;
    private int         fileSystemType_ = UNINITIALIZED;
    int                 serverDatastreamLevel_;
    int                 requestedDatastreamLevel_;
    private long        fileOffset_;
    boolean             isOpen_;
    boolean             isOpenAllowed_ = true;
    private Object      parent_;  // The object that instantiated this IFSDescriptor.
    String              path_ = "";
    byte[]              pathnameBytes_;
    transient Object    serverLock_ = new Object(); 
    AS400Server         server_;  // Note: AS400Server is not serializable.
    private int         shareOption_;
    AS400ImplRemote     system_;
    private Object      fileOffsetLock_ = new Object(); // Semaphore for synchronizing access to fileOffset_.
    private int         maxDataBlockSize_ = 1024;
    private Integer     systemVRM_;
    transient int       errorRC_;  // error return code from most recent request
    int                 patternMatching_ = IFSFile.PATTERN_DEFAULT;  // pattern-matching semantics
  
    // Used for debugging only.  This should always be false for production.
    private static final boolean DEBUG = false;
  
    String fileOwnerName_ = null;

    static
    {
        // Add all byte stream reply data streams of interest to the
        // AS400 server's reply data stream hash table.
        AS400Server.addReplyStream(new IFSCloseRep(), AS400.FILE);
        AS400Server.addReplyStream(new IFSExchangeAttrRep(), AS400.FILE);
        AS400Server.addReplyStream(new IFSLockBytesRep(), AS400.FILE);
        AS400Server.addReplyStream(new IFSReturnCodeRep(), AS400.FILE);
        AS400Server.addReplyStream(new IFSWriteRep(), AS400.FILE);
        AS400Server.addReplyStream(new IFSReadRep(), AS400.FILE);
    }


  // Cast an IFSFileDescriptorImpl object into an IFSFileDescriptorImplRemote.
  // Used by various IFS*ImplRemote classes.
  static IFSFileDescriptorImplRemote castImplToImplRemote(IFSFileDescriptorImpl fd)
  {
      try {
          return (IFSFileDescriptorImplRemote)fd;
      }
      catch (ClassCastException e)
      {
          Trace.log(Trace.ERROR, "Argument is not an instance of IFSFileDescriptorImplRemote", e);
          throw new InternalErrorException(InternalErrorException.UNEXPECTED_EXCEPTION);
      }
  }

  // Determine if the directory entry can be accessed in the specified manner.
  // Returns the returnCode that was set (as a side effect) by createFileHandle().
  int checkAccess(int access, int openOption) throws IOException, AS400SecurityException
  {
      int fileHandle = UNINITIALIZED;
      try {
          fileHandle = createFileHandle(access, openOption);
      } finally {
          if (fileHandle != UNINITIALIZED)
              close(fileHandle); // we don't need this handle anymore
      }
      
      return errorRC_;
  }

  @Override
  public void close()
  {
      try {
          close0();
      }
      catch (IOException e) {
          Trace.log(Trace.ERROR, "Error while closing file " + path_, e);
      }
  }

  public void close0() throws IOException 
  {
      isOpen_ = false;
      close(fileHandle_);
      fileHandle_ = UNINITIALIZED;
  }

  void close(int fileHandle) throws IOException
  {
      if (fileHandle == UNINITIALIZED)
          return;

      // Close the file. Send a close request to the server.
      ClientAccessDataStream ds = null;
      IFSCloseReq req = new IFSCloseReq(fileHandle);
      try {
          ds = (ClientAccessDataStream) server_.sendAndReceive(req);
      }
      catch (ConnectionDroppedException e) {
          Trace.log(Trace.ERROR, "Byte stream connection lost during close", e);
          connectionDropped(e);
      }
      catch (InterruptedException e) {
          Trace.log(Trace.ERROR, "Interrupted", e);
          InterruptedIOException throwException = new InterruptedIOException(e.getMessage());
          throwException.initCause(e);
          throw throwException;
      }

      // Validate the reply.
      if (ds instanceof IFSCloseRep)
      {
          int rc = ((IFSCloseRep) ds).getReturnCode();
          if (rc != 0) {
              Trace.log(Trace.ERROR, "IFSCloseRep return code", rc);
              throw new ExtendedIOException(path_, rc);
          }
      }
      else if (ds instanceof IFSReturnCodeRep) 
      {
          int rc = ((IFSReturnCodeRep) ds).getReturnCode();
          if (rc != IFSReturnCodeRep.SUCCESS) {
              Trace.log(Trace.ERROR, "IFSReturnCodeRep return code", descriptionForReturnCode(rc));
              throw new ExtendedIOException(path_, rc);
          }
      } 
      else
      {
          Trace.log(Trace.ERROR, "Unknown reply data stream", ds.data_);
          throw new InternalErrorException(Integer.toHexString(ds.getReqRepID()), InternalErrorException.DATA_STREAM_UNKNOWN);
      }
  }

  /**
   Establishes communications with the server.
   **/
  void connect() throws IOException, AS400SecurityException
  {
      if (server_ != null)
          return;
      
      if (system_ == null)
          throw new ExtendedIllegalStateException("system", ExtendedIllegalStateException.PROPERTY_NOT_SET);

      try {
          server_ = system_.getConnection(AS400.FILE, false /* forceNewConnection */, false /* skip signon server */ );
      }
      catch (AS400SecurityException e) {
          Trace.log(Trace.ERROR, "Access to byte stream server on '" + system_.getSystemName() + "' denied.", e);
          throw e;
      }

      exchangeServerAttributes();
  }

  /**
   Disconnects from the byte stream server.
   **/
  void connectionDropped(ConnectionDroppedException e) throws ConnectionDroppedException
  {
      if (server_ != null)
      {
          system_.disconnectServer(server_);
          server_ = null;
          try {
              close();
          } 
          catch (Exception exc) { } // Note: Not relevant for IFSFileImplRemote.
      }
      
      Trace.log(Trace.ERROR, "Byte stream connection lost.");
      throw e;
  }

  /**
   * Copies a file or directory to another file or directory.
  **/
  boolean copyTo(String destinationPath, boolean replace) throws AS400SecurityException, IOException
  {
      ClientAccessDataStream ds = null;
      IFSCopyReq req = new IFSCopyReq(path_, destinationPath, replace);
      
      try {
          ds = (ClientAccessDataStream) server_.sendAndReceive(req);
      }
      catch(ConnectionDroppedException e)
      {
          Trace.log(Trace.ERROR, "Byte stream connection lost during copy", e);
          connectionDropped(e);
      }
      catch(InterruptedException e)
      {
          Trace.log(Trace.ERROR, "Interrupted", e);
          InterruptedIOException throwException = new InterruptedIOException(e.getMessage());
          throwException.initCause(e);
          throw throwException;
      }
      
      if (ds instanceof IFSReturnCodeRep)
      {
          int rc = ((IFSReturnCodeRep) ds).getReturnCode();
          if (rc != IFSReturnCodeRep.SUCCESS)
          {
              String path = (rc == IFSReturnCodeRep.DUPLICATE_DIR_ENTRY_NAME ? destinationPath : path_);
              throwSecurityExceptionIfAccessDenied(path,rc); // check for "access denied"
              Trace.log(Trace.ERROR, "IFSReturnCodeRep return code", descriptionForReturnCode(rc));
              throw new ExtendedIOException(path, rc);
          }
          
          return true;
      }

      Trace.log(Trace.ERROR, "Unknown reply data stream", ds.data_);
      throw new InternalErrorException(Integer.toHexString(ds.getReqRepID()), InternalErrorException.DATA_STREAM_UNKNOWN);
  }  

  // Opens the file for 'read' access, and returns a file handle.
  private final int createFileHandle() throws IOException, AS400SecurityException {
      return createFileHandle(IFSOpenReq.READ_ACCESS, IFSOpenReq.OPEN_OPTION_FAIL_OPEN);
  }

  // Opens the file with specified access and option, and returns a file handle.
  // If failure, sets errorRC_ and returns UNINITIALIZED.
  int createFileHandle(int access, int openOption) throws IOException, AS400SecurityException
  {
      int returnCode = IFSReturnCodeRep.FILE_NOT_FOUND;
      int fileHandle = UNINITIALIZED;
      errorRC_ = 0;

      // Try to open the file for the specified type of access.
      try
      {
          // Process an open file request.
          // For 3rd parm (file data CCSID), specify 0 since we don't care about CCSID.
          IFSOpenReq req = new IFSOpenReq(getPathnameAsBytes(), preferredServerCCSID_, 0, access, 
                                          IFSOpenReq.DENY_NONE, IFSOpenReq.NO_CONVERSION, openOption, serverDatastreamLevel_);
          ClientAccessDataStream ds = (ClientAccessDataStream) server_.sendAndReceive(req);
          if (ds instanceof IFSOpenRep)
          {
              // The open was successful.  Close the file if appropriate.
               returnCode = IFSReturnCodeRep.SUCCESS;
               fileHandle = ((IFSOpenRep) ds).getFileHandle();  // @B8a
          }
          else if (ds instanceof IFSReturnCodeRep)
          {
              returnCode = ((IFSReturnCodeRep) ds).getReturnCode();
              throwSecurityExceptionIfAccessDenied(path_,returnCode); // check for "access denied"
          }
          else
          {
              Trace.log(Trace.ERROR, "Unknown reply data stream", ds.data_);
              throw new InternalErrorException(Integer.toHexString(ds.getReqRepID()), InternalErrorException.DATA_STREAM_UNKNOWN);
          }
      }
      catch(ConnectionDroppedException e)
      {
          Trace.log(Trace.ERROR, "Byte stream server connection lost", e);
          connectionDropped(e);
      }
      catch(InterruptedException e)
      {
          Trace.log(Trace.ERROR, "Interrupted", e);
          InterruptedIOException throwException = new InterruptedIOException(e.getMessage());
          throwException.initCause(e);
          throw throwException;
      }

      if (returnCode != IFSReturnCodeRep.SUCCESS)
      {
          if (Trace.isTraceOn() && Trace.isTraceDiagnosticOn())
          {
              Trace.log(Trace.DIAGNOSTIC, "Unable to open file " + path_ + ": " +
                                          "IFSReturnCodeRep return code", descriptionForReturnCode(returnCode));
          }
          
          errorRC_ = returnCode;
          return UNINITIALIZED;
      }

      return fileHandle;
  }

  void exchangeServerAttributes() throws IOException, AS400SecurityException
  {
      synchronized (serverLock_)
      {
          DataStream ds = server_.getExchangeAttrReply();
          IFSExchangeAttrRep rep = null;
          try { 
              rep = (IFSExchangeAttrRep)ds;
          }
          catch (ClassCastException e)
          {
              if (ds instanceof IFSReturnCodeRep)
              {
                  int rc = ((IFSReturnCodeRep) ds).getReturnCode();
                  throwSecurityExceptionIfAccessDenied(path_,rc); // check for "access denied"
                  Trace.log(Trace.ERROR, "IFSReturnCodeRep return code", descriptionForReturnCode(rc));
                  throw new ExtendedIOException(path_, rc);
              }
              else
              {
                  String className = ( ds == null ? "null" : ds.getClass().getName());
                  Trace.log(Trace.ERROR, "Unexpected reply from Exchange Server Attributes: " + className);
                  throw new InternalErrorException(InternalErrorException.DATA_STREAM_UNKNOWN);
              }
          }

          // Note: For releases after V5R4, we ask for Datastream Level 16;
          // for V5R3 thru V5R4, we ask for Datastream Level 8;
          // for V4R5 thru V5R2, we ask for Datastream Level 2;
          // for earlier systems, we ask for Datastream Level 0.
          if (getSystemVRM() >= 0x00060100)
            requestedDatastreamLevel_ = 16;
          else if (getSystemVRM() >= 0x00050300)
            requestedDatastreamLevel_ = 8;
          else if (getSystemVRM() >= 0x00040500)
            requestedDatastreamLevel_ = 2;
          else
            requestedDatastreamLevel_ = 0;
          
          if (rep == null)
          {
              ds = null;
              
              try
              {
                  int[] preferredCcsids;
                  
                  // Datastream level 8 was introduced in release V5R3. Present a list of preferred CCSIDs, if possible.
                  if (getSystemVRM() >= 0x00050300)
                      preferredCcsids = new int[] {0x04b0,0x34b0,0xf200}; // UTF-16, new or old Unicode.
                  else if (getSystemVRM() >= 0x00040500)
                      preferredCcsids = new int[] {0x34b0,0xf200}; // New or old Unicode.
                  else
                  { 
                      // System is pre-V4R5.  Exchange attr's the old way.
                      // Note: Pre-V4R5 systems hang when presented with multiple
                      // preferred CCSIDs in the exchange of attributes.
                      preferredCcsids = new int[] {0xf200}; // Old Unicode only.
                  }

                  // Use GMT date/time, don't use posix style return codes,
                  // use PC pattern matching semantics,
                  // maximum data transfer size of 0xffffffff.
                  ds = server_.sendExchangeAttrRequest(
                         new IFSExchangeAttrReq(true, false,
                                                IFSExchangeAttrReq.PC_PATTERN_MATCH,
                                                0xffffffff,
                                                requestedDatastreamLevel_,
                                                preferredCcsids));
                  rep = (IFSExchangeAttrRep)ds;
              }
              catch(ConnectionDroppedException e)
              {
                  Trace.log(Trace.ERROR, "Byte stream server connection lost");
                  connectionDropped(e);
              }
              catch(InterruptedException e)
              {
                  Trace.log(Trace.ERROR, "Interrupted", e);
                  system_.disconnectServer(server_);
                  server_ = null;
                  InterruptedIOException throwException = new InterruptedIOException(e.getMessage());
                  throwException.initCause(e);
                  throw throwException;
              }
              catch(IOException e)
              {
                  Trace.log(Trace.ERROR, "I/O error during attribute exchange.");
                  system_.disconnectServer(server_);
                  server_ = null;
                  throw e;
              }
              catch(ClassCastException e)
              {
                  if (ds instanceof IFSReturnCodeRep)
                  {
                      int rc = ((IFSReturnCodeRep) ds).getReturnCode();
                      throwSecurityExceptionIfAccessDenied(path_,rc); // check for "access denied"
                      Trace.log(Trace.ERROR, "IFSReturnCodeRep return code", descriptionForReturnCode(rc));
                      throw new ExtendedIOException(path_, rc);
                  }
                  else
                  {
                      String className = ( ds == null ? "null" : ds.getClass().getName());
                      Trace.log(Trace.ERROR, "Unexpected reply from Exchange Server Attributes: " + className);
                      throw new InternalErrorException(InternalErrorException.DATA_STREAM_UNKNOWN);
                  }
              }
          }

          // Process the exchange attributes reply.
          if (rep != null)
          {
              maxDataBlockSize_ = ((IFSExchangeAttrRep) rep).getMaxDataBlockSize();
              preferredServerCCSID_ = rep.getPreferredCCSID();
              serverDatastreamLevel_ = rep.getDataStreamLevel();
              setConverter(ConverterImplRemote.getConverter(preferredServerCCSID_, system_));
              
              if (DEBUG)
              {
                  System.out.println("DEBUG: IFSFileDescriptorImplRemote.exchangeServerAttributes(): " +
                                     "preferredServerCCSID_ == " + preferredServerCCSID_);
                  int[] list = rep.getPreferredCCSIDs();
                  for (int i=0; i 1) {
              if (Trace.traceOn_) Trace.log(Trace.WARNING, "Received multiple replies from listAttributes(fileHandle) (" + replys.size() + ")");
          }
          else {
              reply = (IFSListAttrsRep) replys.elementAt(0);
          }
      }
      finally
      {
          if(!usedGlobalHandle && fileHandle != UNINITIALIZED)
              close(fileHandle);
      }

      return reply;
  }


  /**
   Places a lock on the file at the specified bytes.
   @param offset The first byte of the file to lock (zero is the first byte).
   @param length The number of bytes to lock.
   @return A key for undoing this lock.
   **/
  IFSKey lock(long length) throws IOException, AS400SecurityException {
      return lock(fileOffset_, length);
  }

  IFSKey lock(long offset, long length) throws IOException, AS400SecurityException
  {
      // Assume the arguments have been validated by the caller.

      // Attempt to lock the file.
      ClientAccessDataStream ds = null;
      
      try
      {
          // Issue a mandatory, exclusive lock bytes request.  Mandatory
          // means that the file system enforces the lock by causing any
          // operation which conflicts with the lock to fail.  Exclusive
          // means that only the owner of the lock can read or write the
          // locked area.
          IFSLockBytesReq req = new IFSLockBytesReq(fileHandle_, true, false, offset, length, serverDatastreamLevel_);
          ds = (ClientAccessDataStream) server_.sendAndReceive(req);
      }
      catch(ConnectionDroppedException e)
      {
          Trace.log(Trace.ERROR, "Byte stream server connection lost");
          connectionDropped(e);
      }
      catch(InterruptedException e)
      {
          Trace.log(Trace.ERROR, "Interrupted", e);
          InterruptedIOException throwException = new InterruptedIOException(e.getMessage());
          throwException.initCause(e);
          throw throwException;
      }

      // Verify the reply.
      if (ds instanceof IFSLockBytesRep)
      {
          int rc = ((IFSLockBytesRep) ds).getReturnCode();
          if (rc != 0)
          {
              Trace.log(Trace.ERROR, "IFSLockBytesRep return code", rc);
              throw new ExtendedIOException(path_, rc);
          }
      }
      else if (ds instanceof IFSReturnCodeRep)
      {
          int rc = ((IFSReturnCodeRep) ds).getReturnCode();
          if (rc != IFSReturnCodeRep.SUCCESS)
          {
              throwSecurityExceptionIfAccessDenied(path_,rc); // check for "access denied"
              Trace.log(Trace.ERROR, "IFSReturnCodeRep return code", descriptionForReturnCode(rc));
              throw new ExtendedIOException(path_, rc);
          }
      }
      else
      {
          Trace.log(Trace.ERROR, "Unknown reply data stream", ds.data_);
          throw new InternalErrorException(Integer.toHexString(ds.getReqRepID()), InternalErrorException.DATA_STREAM_UNKNOWN);
      }

      // Generate the key for this lock.
      IFSKey key = new IFSKey(fileHandle_, offset, length, true);

      return key;
  }

  /**
   Reads up to length bytes of data from this input stream into data,
   starting at the array offset dataOffset.
   @param data The buffer into which the data is read.
   @param offset The start offset of the data in the buffer.
   @param length The maximum number of bytes to read
   @return The total number of bytes read into the buffer, or -1 if there is no more data because the end of file has been reached.
   **/
  int read(byte[] data, int dataOffset, int length) throws IOException, AS400SecurityException
  {
      // Assume the arguments have been validated by the public class.

      // If length is zero then return zero.
      if (length == 0)
          return 0;

      int totalBytesRead = 0;
      int bytesRemainingToRead = length;
      boolean endOfFile = false;
      
      while (totalBytesRead < length && !endOfFile)
      {
          // If the number of bytes being requested is greater than 16 million, then submit multiple requests for smaller chunks.  
          // The File Server has a limit that is somewhat below 16 megabytes (allowing for headers, etc), so 16 _million_ bytes is a safe limit.

          // Issue the read data request.
          int bytesToReadThisTime = Math.min(bytesRemainingToRead, MAX_BYTES_PER_READ);
          IFSReadReq req = new IFSReadReq(fileHandle_, fileOffset_, bytesToReadThisTime, serverDatastreamLevel_);
          ClientAccessDataStream ds = null;
          
          try{
              ds = (ClientAccessDataStream) server_.sendAndReceive(req);
          }
          catch(ConnectionDroppedException e)
          {
              Trace.log(Trace.ERROR, "Byte stream server connection lost");
              connectionDropped(e);
          }
          catch(InterruptedException e)
          {
              Trace.log(Trace.ERROR, "Interrupted", e);
              InterruptedIOException throwException = new InterruptedIOException(e.getMessage());
              throwException.initCause(e);
              throw throwException;
          }

          // Receive replies until the end of chain.
          boolean endOfChain = false;
          int bytesReadByThisRequest = 0;
          
          do
          {
              if (ds instanceof IFSReadRep)
              {
                  // Copy the data from the reply to the data parameter.
                  byte[] buffer = ((IFSReadRep) ds).getData();
                  if (buffer.length > 0)
                  {
                      System.arraycopy(buffer, 0, data, dataOffset, buffer.length);
                      bytesReadByThisRequest += buffer.length;
                      dataOffset += buffer.length;
                  }
                  else // no data returned. This implies end-of-file (e.g. if file is empty).
                  {
                      bytesReadByThisRequest = -1;
                      endOfFile = true;
                  }
              }
              else if (ds instanceof IFSReturnCodeRep)
              {
                  // Check for failure.
                  int rc = ((IFSReturnCodeRep) ds).getReturnCode();

                  if (rc == IFSReturnCodeRep.SUCCESS)
                  {  // It worked, so nothing special to do here.
                  }
                  else if (rc == IFSReturnCodeRep.NO_MORE_DATA)
                  {
                      // End of file.
                      bytesReadByThisRequest = -1;
                      endOfFile = true;
                  }
                  else  // none of the above
                  {
                      throwSecurityExceptionIfAccessDenied(path_,rc); // check for "access denied"
                      Trace.log(Trace.ERROR, "IFSReturnCodeRep return code", descriptionForReturnCode(rc));
                      throw new ExtendedIOException(path_, rc);
                  }
              }
              else
              {
                  Trace.log(Trace.ERROR, "Unknown reply data stream", ds.data_);
                  throw new InternalErrorException(Integer.toHexString(ds.getReqRepID()), InternalErrorException.DATA_STREAM_UNKNOWN);
              }

              // Get the next reply if not end of chain.
              endOfChain = ((IFSDataStream) ds).isEndOfChain();
              if (!endOfChain)
              {
                  try {
                      ds = (ClientAccessDataStream) server_.receive(req.getCorrelation());
                  }
                  catch(ConnectionDroppedException e)
                  {
                      Trace.log(Trace.ERROR, "Byte stream server connection lost");
                      connectionDropped(e);
                  }
                  catch(InterruptedException e)
                  {
                      Trace.log(Trace.ERROR, "Interrupted", e);
                      InterruptedIOException throwException = new InterruptedIOException(e.getMessage());
                      throwException.initCause(e);
                      throw throwException;
                  }
              }
          }
          while (!endOfChain);

          // Advance the file pointer.
          if (bytesReadByThisRequest > 0)
          {
              incrementFileOffset(bytesReadByThisRequest);
              totalBytesRead += bytesReadByThisRequest;
              bytesRemainingToRead -= bytesReadByThisRequest;
          }
      }

      // If we have read zero bytes and hit end-of-file, indicate that by returning -1.
      // Otherwise return total number of bytes read.
      return (endOfFile && totalBytesRead == 0 ? -1 : totalBytesRead);
  }

  void setConverter(ConverterImplRemote converter) {
    converter_ = converter;
  }

  void setCCSID(int ccsid) throws IOException {
      fileDataCCSID_ = ccsid;
  }

  public void setFileOffset(long fileOffset)
  {
      synchronized (fileOffsetLock_) {
          fileOffset_ = fileOffset;
      }
  }

  boolean setLength(long length) throws IOException, AS400SecurityException
  {
      // Assume that we are connected to the server.

      // Prepare to issue a 'change attributes' request.
      ClientAccessDataStream ds = null;
      int fileHandle = UNINITIALIZED;

      try
      {
          // Open the file for read/write, get a file handle, and call 'change attributes'.
          fileHandle = createFileHandle(IFSOpenReq.WRITE_ACCESS, IFSOpenReq.OPEN_OPTION_FAIL_OPEN);
          if (fileHandle == UNINITIALIZED)
          {
              if (Trace.traceOn_) Trace.log(Trace.ERROR, "Unable to create handle to file " + path_ + ". IFSReturnCodeRep return code", errorRC_);
              return false;
          }
          
          IFSChangeAttrsReq req = new IFSChangeAttrsReq(fileHandle, length, serverDatastreamLevel_);
          ds = (ClientAccessDataStream) server_.sendAndReceive(req);
      }
      catch(ConnectionDroppedException e)
      {
          Trace.log(Trace.ERROR, "Byte stream server connection lost.");
          connectionDropped(e);
      }
      catch(InterruptedException e)
      {
          Trace.log(Trace.ERROR, "Interrupted");
          InterruptedIOException throwException = new InterruptedIOException(e.getMessage());
          throwException.initCause(e);
          throw throwException;
      }
      finally {
          close(fileHandle);  // we don't need this handle anymore
      }

      // Verify the reply.
      boolean success = false;
      if (ds instanceof IFSReturnCodeRep)
      {
          int rc = ((IFSReturnCodeRep) ds).getReturnCode();
          if (rc == IFSReturnCodeRep.SUCCESS)
              success = true;
          else
          {
              Trace.log(Trace.ERROR, path_ + ": IFSReturnCodeRep return code", descriptionForReturnCode(rc));

              throwSecurityExceptionIfAccessDenied(path_,rc); // check for "access denied"
          }
      }
      else
      {
          Trace.log(Trace.ERROR, "Unknown reply data stream", ds.data_);
          throw new InternalErrorException(Integer.toHexString(ds.getReqRepID()), InternalErrorException.DATA_STREAM_UNKNOWN);
      }

      // Back off the file pointer if needed.
      if (fileOffset_ > length)
          fileOffset_ = length;

      return success;
  }

  // Ignores fileHandle if state==false.
  void setOpen(boolean state, int fileHandle)
  {
      if (state == true)
      {
          if (fileHandle == UNINITIALIZED)
          {
              Trace.log(Trace.ERROR, "Called setOpen with invalid file handle: " + fileHandle);
              throw new InternalErrorException(InternalErrorException.UNEXPECTED_EXCEPTION);
          }
          else
          {
              if (fileHandle != fileHandle_) close(); // close currently-open handle if different
              fileHandle_ = fileHandle;
          }
      }
      
      isOpen_ = state;
  }

  void setOpenAllowed(boolean state) {
      isOpenAllowed_ = state;
  }

  void setPreferredCCSID(int ccsid) {
      preferredServerCCSID_ = ccsid;
  }

  void setServer(AS400Server server) {
      server_ = server;
  }
   
  @Override
  public void sync() throws IOException
  {
      // Note: UserSpaceImplRemote creates an IFSFileDescriptorImplRemote directly.

      if (parent_ == null)
          Trace.log(Trace.ERROR, "IFSFileDescriptor.sync() was called when parent is null.");
      else if (parent_ instanceof IFSRandomAccessFileImplRemote)
          ((IFSRandomAccessFileImplRemote)parent_).flush();
      else if (parent_ instanceof IFSFileOutputStreamImplRemote)
          ((IFSFileOutputStreamImplRemote)parent_).flush();
      else
      {
          Trace.log(Trace.WARNING, "IFSFileDescriptor.sync() was called " +
                                   "when parent is neither an IFSRandomAccessFile nor an IFSFileOutputStream.");
      }
  }

  private static final void throwSecurityExceptionIfAccessDenied(String path, int returnCode) throws AS400SecurityException
  {
      if (returnCode == IFSReturnCodeRep.ACCESS_DENIED_TO_DIR_ENTRY ||
          returnCode == IFSReturnCodeRep.ACCESS_DENIED_TO_REQUEST)
      {
          if (Trace.traceOn_) Trace.log(Trace.DIAGNOSTIC, "Access denied to file " + path + ". " +
                                                          "IFSReturnCodeRep return code", returnCode);
          throw new AS400SecurityException(path, AS400SecurityException.DIRECTORY_ENTRY_ACCESS_DENIED);
      }
  }


  /**
   Undoes a lock on this file.
   @param key The key for the lock.
   **/
  void unlock(IFSKey key) throws IOException, AS400SecurityException
  {
      // Assume the argument has been validated by the caller.

      // Verify that this key is compatible with this file.
      if (key.fileHandle_ != fileHandle_)
      {
          Trace.log(Trace.ERROR, "Attempt to use IFSKey on different file stream.");
          throw new ExtendedIllegalArgumentException("key", ExtendedIllegalArgumentException.PARAMETER_VALUE_NOT_VALID);
      }

      // Attempt to unlock the file.
      ClientAccessDataStream ds = null;
      IFSUnlockBytesReq req = new IFSUnlockBytesReq(key.fileHandle_, key.offset_,
                                                    key.length_, key.isMandatory_, serverDatastreamLevel_);
       try {
           ds = (ClientAccessDataStream) server_.sendAndReceive(req);
       }
       catch(ConnectionDroppedException e)
       {
           Trace.log(Trace.ERROR, "Byte stream server connection lost");
           connectionDropped(e);
       }
       catch(InterruptedException e)
       {
           Trace.log(Trace.ERROR, "Interrupted", e);
           InterruptedIOException throwException = new InterruptedIOException(e.getMessage());
           throwException.initCause(e);
           throw throwException;
       }

       // Verify the reply.
       if (ds instanceof IFSReturnCodeRep)
       {
           int rc = ((IFSReturnCodeRep) ds).getReturnCode();
           if (rc != IFSReturnCodeRep.SUCCESS)
           {
               throwSecurityExceptionIfAccessDenied(path_,rc); // check for "access denied"
               Trace.log(Trace.ERROR, "IFSReturnCodeRep return code", descriptionForReturnCode(rc));
               throw new ExtendedIOException(path_, rc);
           }
       }
       else
       {
           Trace.log(Trace.ERROR, "Unknown reply data stream", ds.data_);
           throw new InternalErrorException(Integer.toHexString(ds.getReqRepID()), InternalErrorException.DATA_STREAM_UNKNOWN);
       }
  }


  // Common code used by IFSFileOutputStreamImplRemote and IFSRandomAccessFileImplRemote.
  /**
   Writes length bytes from the byte array data, starting at dataOffset, to this File.
   @param data The data.
   @param dataOffset The start offset in the data.
   @param length The number of bytes to write.
   @parm forceToStorage Whether data must be written before the server replies.
   **/
  void writeBytes(byte[] data, int dataOffset, int length) throws IOException, AS400SecurityException {
      writeBytes(data, dataOffset, length, false);
  }
  
  void writeBytes(byte[] data,  int dataOffset, int length, boolean forceToStorage) throws IOException, AS400SecurityException
  {
      // Assume the arguments have been validated by the caller.

      // Send write requests until all data has been written.
      while(length > 0)
      {
          // Determine how much data can be written on this request.
          int writeLength = (length > maxDataBlockSize_ ? maxDataBlockSize_ : length);

          // Build the write request.  Set the chain bit if there is
          // more data to write.
          IFSWriteReq req = new IFSWriteReq(fileHandle_, fileOffset_,
                                            data, dataOffset, writeLength,
                                            0xffff, forceToStorage, serverDatastreamLevel_);
          if (length - writeLength > 0)
          {
              // Indicate that there is more to write.
              req.setChainIndicator(1);
          }

          // Send the request.
          ClientAccessDataStream ds = null;
          try {
              ds = (ClientAccessDataStream) server_.sendAndReceive(req);
          }
          catch(ConnectionDroppedException e)
          {
              Trace.log(Trace.ERROR, "Byte stream server connection lost");
              connectionDropped(e);
          }
          catch(InterruptedException e)
          {
              Trace.log(Trace.ERROR, "Interrupted", e);
              InterruptedIOException throwException = new InterruptedIOException(e.getMessage());
              throwException.initCause(e);
              throw throwException;
          }

          // Check the reply.
          if (ds instanceof IFSWriteRep)
          {
               IFSWriteRep rep = (IFSWriteRep) ds;
               int rc = rep.getReturnCode();
               if (rc != 0)
               {
                   Trace.log(Trace.ERROR, "IFSWriteRep return code", rc);
                   throw new ExtendedIOException(path_, rc);
               }

               // Advance the file pointer the length of the data written.
               int lengthWritten = writeLength - rep.getLengthNotWritten();
               incrementFileOffset(lengthWritten);
               dataOffset += lengthWritten;
               length -= lengthWritten;

               // Ensure that all data requested was written.
               if (lengthWritten != writeLength)
               {
                   Trace.log(Trace.ERROR, "Incomplete write.  Only " +
                                          Integer.toString(lengthWritten) + " bytes of a requested " +
                                          Integer.toString(writeLength) + " were written.");
                   throw new ExtendedIOException(path_, ExtendedIOException.UNKNOWN_ERROR);
               }
          }
          else if (ds instanceof IFSReturnCodeRep)
          {
              int rc = ((IFSReturnCodeRep) ds).getReturnCode();
              if (rc != IFSReturnCodeRep.SUCCESS)
              {
                  throwSecurityExceptionIfAccessDenied(path_,rc); // check for "access denied"
                  Trace.log(Trace.ERROR, "IFSReturnCodeRep return code", descriptionForReturnCode(rc));
                  throw new ExtendedIOException(path_, rc);
              }
          }
          else
          {
              Trace.log(Trace.ERROR, "Unknown reply data stream", ds.data_);
              throw new InternalErrorException(Integer.toHexString(ds.getReqRepID()), InternalErrorException.DATA_STREAM_UNKNOWN);
          }
      }
  }
  
  public int getServerDatastreamLevel() {
      return serverDatastreamLevel_;
  }
  
  public void freeHandle(int objectHandle) throws IOException, AS400SecurityException{
      IFSFreeHandleReq req = new IFSFreeHandleReq(objectHandle);
      server_.send(req);
  }
  
  public int getASP() throws IOException, AS400SecurityException 
  {
      if (fileAsp_ == UNINITIALIZED)
      { 
          ClientAccessDataStream ds = null;
          int rc = 0;

          connect();

          // Ensure that we are connected to the server.
          byte[] pathname = getConverter().stringToByteArray(path_);

          //create user handle
          int userHandle = UNINITIALIZED, objectHandle = UNINITIALIZED;
          try
          {
              // In 7.5 and prior releases, need to create user handle for IFS tables to be initialized.
              userHandle = (getSystem().getVRM() <= 0x00070500) ? system_.createUserHandle() : 0;
              
              try
              {
                  // Issue a Look up request to create an object handle.
                  IFSLookupReq req = new IFSLookupReq(pathname, preferredServerCCSID_, userHandle, IFSLookupReq.OA12, IFSObjAttrs1.OWNERANAME_ASP_FLAS, 0); 
                  ds = (ClientAccessDataStream) server_.sendAndReceive(req);
              }
              catch(ConnectionDroppedException e)
              {
                  Trace.log(Trace.ERROR, "Byte stream server connection lost.");
                  connectionDropped(e);
              }
              catch(InterruptedException e)
              {
                  Trace.log(Trace.ERROR, "Interrupted");
                  InterruptedIOException throwException = new InterruptedIOException(e.getMessage());
                  throwException.initCause(e);
                  throw throwException;
              }

              rc = 0;
              if (ds instanceof IFSLookupRep)
              {
                  objectHandle = ((IFSLookupRep) ds).getHandle();
                  retrieveAttributes(ds, objectHandle);  //@AC7A 
              }
              else if (ds instanceof IFSReturnCodeRep)
              {
                  rc = ((IFSReturnCodeRep) ds).getReturnCode();
                  if (rc != IFSReturnCodeRep.SUCCESS)
                      Trace.log(Trace.ERROR, "IFSReturnCodeRep return code", rc);

                  throw new ExtendedIOException(path_, rc);
              }
              else
              {
                  Trace.log(Trace.ERROR, "Unknown reply data stream", ds.getReqRepID());
                  throw new InternalErrorException(Integer.toHexString(ds.getReqRepID()), InternalErrorException.DATA_STREAM_UNKNOWN);
              }
          }
          finally
          {
              if (objectHandle != UNINITIALIZED)
                  freeHandle(objectHandle);
          }
      }

      return fileAsp_;
  }
  
  public String getOwnerNameByUserHandle(boolean forceRetrieve) throws IOException, AS400SecurityException
  {
      Trace.log(Trace.INFORMATION, "Owner Name " + (fileOwnerName_ == null? "is null" : fileOwnerName_));
      
      if (forceRetrieve || fileOwnerName_ == null)
      {
          Trace.log(Trace.INFORMATION, "force retrieve for Owner Name.");
          ClientAccessDataStream ds = null;
          int rc = 0;

          // Ensure that we are connected to the server.
          connect();
          byte[] pathname = getConverter().stringToByteArray(path_);
        
          //create user handle
          int userHandle = UNINITIALIZED, objectHandle = UNINITIALIZED;
          try
          {
              // In 7.5 and prior releases, need to create user handle for IFS tables to be initialized.
              userHandle = (getSystem().getVRM() <= 0x00070500) ? system_.createUserHandle() : 0;
              
              try
              {
                  // Issue a Look up request to create an object handle.
                  IFSLookupReq req = new IFSLookupReq(pathname, preferredServerCCSID_, userHandle, IFSLookupReq.OA12, IFSObjAttrs1.OWNERANAME_ASP_FLAS, 0); 
                  ds = (ClientAccessDataStream) server_.sendAndReceive(req);
              }
              catch(ConnectionDroppedException e)
              {
                  Trace.log(Trace.ERROR, "Byte stream server connection lost.");
                  connectionDropped(e);
              }
              catch(InterruptedException e)
              {
                  Trace.log(Trace.ERROR, "Interrupted");
                  InterruptedIOException throwException = new InterruptedIOException(e.getMessage());
                  throwException.initCause(e);
                  throw throwException;
              }

              // Verify that we got a handle back.
              rc = 0;
              if (ds instanceof IFSLookupRep)
              {
                  objectHandle = ((IFSLookupRep) ds).getHandle();
                  retrieveAttributes(ds, objectHandle);  //@AC7A
              }
              else if (ds instanceof IFSReturnCodeRep)
              {
                  rc = ((IFSReturnCodeRep) ds).getReturnCode();
                  if (rc != IFSReturnCodeRep.SUCCESS) Trace.log(Trace.ERROR, "IFSReturnCodeRep return code", rc);
                  throw new ExtendedIOException(path_, rc);
              } 
              else
              {
                  Trace.log(Trace.ERROR, "Unknown reply data stream", ds.getReqRepID());
                  throw new InternalErrorException(Integer.toHexString(ds.getReqRepID()), InternalErrorException.DATA_STREAM_UNKNOWN);
              }
          }
          finally
          {
              if(objectHandle != UNINITIALIZED)
                  freeHandle(objectHandle);
          }
      }
      
      return (fileOwnerName_ == null ? "" : fileOwnerName_);
  }
  
  public String getFileSystemType() throws IOException, AS400SecurityException
  {
      String typeString = "Unknown";
    
      if (fileSystemType_ == UNINITIALIZED)
      {
          ClientAccessDataStream ds = null;
          int rc = 0;
          connect();

          int objectHandle = UNINITIALIZED;
          byte[] pathname = getConverter().stringToByteArray(path_);

          //create user handle
          int userHandle = UNINITIALIZED;
          try
          {
              userHandle = (getSystem().getVRM() <= 0x00070500) ? system_.createUserHandle() : 0;
              
              try
              {
                  // Issue a Look up request to create an object handle.
                  IFSLookupReq req = new IFSLookupReq(pathname, preferredServerCCSID_, userHandle, IFSLookupReq.OA12, IFSObjAttrs1.OWNERANAME_ASP_FLAS, 0);
                  ds = (ClientAccessDataStream) server_.sendAndReceive(req);
              }
              catch(ConnectionDroppedException e)
              {
                  Trace.log(Trace.ERROR, "Byte stream server connection lost.");
                  connectionDropped(e);
              }
              catch(InterruptedException e)
              {
                  Trace.log(Trace.ERROR, "Interrupted");
                  InterruptedIOException throwException = new InterruptedIOException(e.getMessage());
                  throwException.initCause(e);
                  throw throwException;
              }

              rc = 0;
              if (ds instanceof IFSLookupRep)
              {
                  objectHandle = ((IFSLookupRep) ds).getHandle();
                  retrieveAttributes(ds, objectHandle); //@AC7A 
              }
              else if (ds instanceof IFSReturnCodeRep)
              {
                  rc = ((IFSReturnCodeRep) ds).getReturnCode();
                  if (rc != IFSReturnCodeRep.SUCCESS)
                      Trace.log(Trace.ERROR, "IFSReturnCodeRep return code", rc);

                  throw new ExtendedIOException(path_, rc);
              }
              else
              {
                  Trace.log(Trace.ERROR, "Unknown reply data stream", ds.getReqRepID());
                  throw new InternalErrorException(Integer.toHexString(ds.getReqRepID()), InternalErrorException.DATA_STREAM_UNKNOWN);
              }
          }
          finally
          {
              if (objectHandle != UNINITIALIZED)
                  freeHandle(objectHandle);
          }
      }
      
    switch(fileSystemType_)
    {
        case 1 : typeString = "EPFS"; break;
        case 2 : typeString = "QDLS"; break;
        case 3 : typeString = "QSYS"; break;
        case 4 : typeString = "NFS"; break;
        case 5 : typeString = "LRFS"; break;
        case 6 : typeString = "QOPT"; break;
        case 7 : typeString = "QRFS"; break;
        case 9 : typeString = "EPFSP"; break;
        case 11 : typeString = "QNETC"; break;
        case 12 : typeString = "QDTL"; break;
        case 13 : typeString = "IEPFS"; break;
        case 14 : typeString = "ASPQSYS"; break;
    }
    
    return typeString;
  }

  private void retrieveAttributes(ClientAccessDataStream ds, int objectHandle) throws IOException, AS400SecurityException
  {
      fileAsp_ = ((IFSLookupRep) ds).getASP();
      if (fileAsp_ < 1)  fileAsp_ = UNINITIALIZED;
      
      fileOwnerName_ = ((IFSLookupRep) ds).getOwnerName(system_.getCcsid());
      
      fileDataCCSID_ = ((IFSLookupRep) ds).getCCSID(serverDatastreamLevel_);
      if (fileDataCCSID_ < 1)  fileDataCCSID_ = UNINITIALIZED;

      int userHandle =  (getSystem().getVRM() <= 0x00070500) ? system_.getUserHandle() : 0;    
      
      int rc = 0;
      ds = null;
      try
      {
          IFSGetFileSystemReq req = new IFSGetFileSystemReq(objectHandle, userHandle);
          ds = (ClientAccessDataStream) server_.sendAndReceive(req);
      }
      catch(ConnectionDroppedException e)
      {
          Trace.log(Trace.ERROR, "Byte stream server connection lost.");
          connectionDropped(e);
      }
      catch(InterruptedException e)
      {
          Trace.log(Trace.ERROR, "Interrupted");
          InterruptedIOException throwException = new InterruptedIOException(e.getMessage());
          throwException.initCause(e);
          throw throwException;
      }

      // Verify the reply.
      rc = 0;
      if (ds instanceof IFSGetFileSystemRep)
          fileSystemType_ = ((IFSGetFileSystemRep) ds).getFileSystemType();
      else if (ds instanceof IFSReturnCodeRep)
      {
          rc = ((IFSReturnCodeRep) ds).getReturnCode();
          if (rc != IFSReturnCodeRep.SUCCESS) Trace.log(Trace.ERROR, "IFSReturnCodeRep return code", rc);
          throw new ExtendedIOException(path_, rc);
      }
      else
      {
          Trace.log(Trace.ERROR, "Unknown reply data stream", ds.getReqRepID());
          throw new InternalErrorException(InternalErrorException.DATA_STREAM_UNKNOWN, Integer.toHexString(ds.getReqRepID()),null);
      }
  }
}








© 2015 - 2025 Weber Informatics LLC | Privacy Policy