/**
   * Load a file
   *
   * @param req FileRequest
   * @return int
   * @exception Exception
   */
  public int loadFile(FileRequest req) throws Exception {

    // DEBUG

    long startTime = 0L;
    SingleFileRequest loadReq = (SingleFileRequest) req;

    if (Debug.EnableInfo && hasDebug()) {
      Debug.println(
          "## ObjIdLoader loadFile() req="
              + loadReq.toString()
              + ", thread="
              + Thread.currentThread().getName());
      startTime = System.currentTimeMillis();
    }

    // Check if the temporary file still exists, if not then the file has been deleted from the
    // filesystem

    File tempFile = new File(loadReq.getTemporaryFile());
    FileSegment fileSeg = findFileSegmentForPath(loadReq.getVirtualPath());

    if (tempFile.exists() == false || fileSeg == null) {

      // DEBUG

      if (Debug.EnableInfo && hasDebug()) Debug.println("  Temporary file deleted");

      // Return an error status

      fileSeg.setStatus(FileSegmentInfo.Error, false);
      return StsError;
    }

    // DEBUG

    if (Debug.EnableInfo && hasDebug())
      Debug.println(
          "## ObjIdLoader fileSeg="
              + fileSeg.getTemporaryFile()
              + ", virtPath="
              + loadReq.getVirtualPath());

    // Load the file data

    int loadSts = StsRequeue;
    String objectId = null;

    int fileId = loadReq.getFileId();
    int strmId = loadReq.getStreamId();

    try {

      // Update the segment status

      fileSeg.setStatus(FileSegmentInfo.Loading);

      // Get the object id for the file

      objectId = getDBObjectIdInterface().loadObjectId(fileId, strmId);

      if (objectId != null) {

        // Load the file data

        loadFileData(fileId, strmId, objectId, fileSeg);

        // Set the load status

        loadSts = StsSuccess;

        // DEBUG

        if (Debug.EnableInfo && hasDebug()) {
          long endTime = System.currentTimeMillis();
          Debug.println(
              "## ObjIdLoader loaded fid="
                  + loadReq.getFileId()
                  + ", stream="
                  + loadReq.getStreamId()
                  + ", time="
                  + (endTime - startTime)
                  + "ms");
        }
      } else {

        // DEBUG

        if (Debug.EnableInfo && hasDebug())
          Debug.println(
              "## ObjIdLoader No object id mapping for fid="
                  + loadReq.getFileId()
                  + ", stream="
                  + loadReq.getStreamId());

        // Indicate a load success

        loadSts = StsSuccess;
      }
    } catch (DBException ex) {

      // DEBUG

      if (Debug.EnableError && hasDebug()) Debug.println(ex);

      // Indicate the file load failed

      loadSts = StsError;
    } catch (FileOfflineException ex) {

      // DEBUG

      if (Debug.EnableError && hasDebug()) Debug.println(ex);

      // Indicate the file load failed

      loadSts = StsError;
    } catch (IOException ex) {

      // DEBUG

      if (Debug.EnableError && hasDebug()) Debug.println(ex);

      // Indicate the file load failed

      loadSts = StsRequeue;
    } catch (Exception ex) {

      // DEBUG

      if (Debug.EnableError && hasDebug()) Debug.println(ex);

      // Indicate the file load failed

      loadSts = StsError;
    }

    // Clear the last modified date/time of the temporary file to indicate it has not been updated

    tempFile.setLastModified(0L);

    // Check if the file was loaded successfully

    if (loadSts == StsSuccess) {

      // Signal that the file data is available

      fileSeg.signalDataAvailable();

      // Update the file status

      fileSeg.setStatus(FileSegmentInfo.Available, false);

      // Run the file load processors

      runFileLoadedProcessors(getContext(), loadReq.getFileState(), fileSeg);
    } else if (loadSts == StsError) {

      // Set the file status to indicate error to any client reading threads

      fileSeg.setStatus(FileSegmentInfo.Error, false);

      // Wakeup any threads waiting on data for this file

      fileSeg.setReadableLength(0L);
      fileSeg.signalDataAvailable();

      // Delete the temporary file

      fileSeg.deleteTemporaryFile();
    }

    // Return the load file status

    return loadSts;
  }
  /**
   * Store a file
   *
   * @param req FileRequest
   * @return int
   * @exception Exception
   */
  public int storeFile(FileRequest req) throws Exception {

    // Check for a single file request

    int saveSts = StsError;
    SingleFileRequest saveReq = (SingleFileRequest) req;

    // Check if the temporary file still exists, if not then the file has been deleted from the
    // filesystem

    File tempFile = new File(saveReq.getTemporaryFile());
    FileSegment fileSeg = findFileSegmentForPath(saveReq.getVirtualPath());

    if (tempFile.exists() == false || fileSeg == null) {

      // DEBUG

      if (Debug.EnableInfo && hasDebug()) Debug.println("  Temporary file deleted");

      // Return an error status

      return StsError;
    }

    // Run any file store processors

    runFileStoreProcessors(m_dbCtx, saveReq.getFileState(), fileSeg);

    // Update the segment status, and clear the updated flag

    fileSeg.setStatus(FileSegmentInfo.Saving);
    fileSeg.getInfo().setUpdated(false);

    // Save the file data

    try {

      // Save the file data and get the assigned object id

      String objectId =
          saveFileData(saveReq.getFileId(), saveReq.getStreamId(), fileSeg, req.getAttributes());

      // Save the object id to the mapping database

      getDBObjectIdInterface().saveObjectId(saveReq.getFileId(), saveReq.getStreamId(), objectId);

      // Indicate that the save was successful

      saveSts = StsSuccess;
    } catch (DBException ex) {

      // DEBUG

      if (Debug.EnableError && hasDebug()) Debug.println(ex);

      // Indicate the file save failed

      saveSts = StsError;
    } catch (IOException ex) {

      // DEBUG

      if (Debug.EnableError && hasDebug()) Debug.println(ex);

      // Indicate the file save failed

      saveSts = StsError;
    }

    // Update the segment status

    if (saveSts == StsSuccess) fileSeg.setStatus(FileSegmentInfo.Saved, false);
    else fileSeg.setStatus(FileSegmentInfo.Error, false);

    // Return the data save status

    return saveSts;
  }
  /**
   * Close the network file
   *
   * @param sess SrvSession
   * @param netFile NetworkFile
   * @exception IOException
   */
  public void closeFile(SrvSession sess, NetworkFile netFile) throws IOException {

    // Close the cached network file

    if (netFile instanceof CachedNetworkFile) {

      // Get the cached network file

      CachedNetworkFile cacheFile = (CachedNetworkFile) netFile;
      cacheFile.closeFile();

      // Get the file segment details

      FileSegment fileSeg = cacheFile.getFileSegment();

      // Check if the file data has been updated, if so then queue a file save

      if (fileSeg.isUpdated() && netFile.hasDeleteOnClose() == false) {

        // Set the modified date/time and file size for the file

        File tempFile = new File(fileSeg.getTemporaryFile());

        netFile.setModifyDate(tempFile.lastModified());
        netFile.setFileSize(tempFile.length());

        // Queue a file save request to save the data back to the repository, if not already queued

        if (fileSeg.isSaveQueued() == false) {

          // Create a file save request for the updated file segment

          SingleFileRequest fileReq =
              new SingleFileRequest(
                  FileRequest.SAVE,
                  cacheFile.getFileId(),
                  cacheFile.getStreamId(),
                  fileSeg.getInfo(),
                  netFile.getFullName(),
                  cacheFile.getFileState());

          // Check if there are any attributes to be added to the file request

          if (hasRequiredAttributes() && sess != null) {

            // Check if the user name is required

            if (m_reqAttributes.containsString(FileRequest.AttrUserName)) {

              // Add the user name attribute

              ClientInfo cInfo = sess.getClientInformation();
              String userName = "";

              if (cInfo != null && cInfo.getUserName() != null) userName = cInfo.getUserName();

              fileReq.addAttribute(new NameValue(FileRequest.AttrUserName, userName));
            }

            // Check if the protocol type is required

            if (m_reqAttributes.containsString(FileRequest.AttrProtocol)) {

              // Add the protocol type attribute

              fileReq.addAttribute(new NameValue(FileRequest.AttrProtocol, sess.getProtocolName()));
            }
          }

          // Set the file segment status

          fileSeg.setStatus(FileSegmentInfo.SaveWait, true);

          // Queue the file save request

          queueFileRequest(fileReq);
        } else if (Debug.EnableInfo && hasDebug()) {

          // DEBUG

          Debug.println("## FileLoader Save already queued for " + fileSeg);
        }
      }

      // Update the cache timeout for the temporary file if there are no references to the file. If
      // the file was
      // opened for sequential access only it will be expired quicker.

      else if (cacheFile.getFileState().getOpenCount() == 0) {

        // If the file was opened for sequential access only then we can delete it from the
        // temporary area
        // sooner

        long tmo = System.currentTimeMillis();

        if (cacheFile.isSequentialOnly()) tmo += SequentialFileExpire;
        else tmo += m_stateCache.getFileStateExpireInterval();

        // Set the file state expiry, the local file data will be deleted when the file state
        // expires (if there
        // are still no references to the file).

        cacheFile.getFileState().setExpiryTime(tmo);
      }

      // If the database is not online and the file is marked for delete then queue a delete file
      // request to do
      // the
      // delete when the database is back online

      if (m_dbCtx.isAvailable() == false && netFile.hasDeleteOnClose()) {

        // Queue an offline delete request for the file

        DeleteFileRequest deleteReq =
            new DeleteFileRequest(
                cacheFile.getFileId(),
                cacheFile.getStreamId(),
                fileSeg.getTemporaryFile(),
                cacheFile.getFullNameStream(),
                cacheFile.getFileState());
        m_dbCtx.addOfflineFileDelete(deleteReq);

        // DEBUG

        if (Debug.EnableInfo && hasDebug())
          Debug.println("## FileLoader queued offline delete, " + deleteReq);
      }
    }
  }