public synchronized void appendItemToQueue(final FrostDownloadItem item) {
      final String id = item.getGqIdentifier();
      directGETsInProgress.add(id);

      queue.addLast(item);
      notifyAll(); // notify all waiters (if any) of new record
    }
  public boolean startDownload(final FrostDownloadItem dlItem) {

    if (dlItem == null || dlItem.getState() != FrostDownloadItem.STATE_WAITING) {
      return false;
    }

    dlItem.setDownloadStartedTime(System.currentTimeMillis());

    dlItem.setState(FrostDownloadItem.STATE_PROGRESS);

    final String gqid = dlItem.getGqIdentifier();
    final File targetFile = new File(dlItem.getDownloadFilename());
    boolean isDda =
        fcpTools.startPersistentGet(dlItem.getKey(), gqid, targetFile, dlItem.getPriority());
    dlItem.setDirect(!isDda);

    return true;
  }
  /** Must be called after the upload and download model is initialized! */
  public PersistenceManager(final UploadModel um, final DownloadModel dm) throws Throwable {

    showExternalItemsDownload =
        Core.frostSettings.getBoolValue(SettingsClass.GQ_SHOW_EXTERNAL_ITEMS_DOWNLOAD);
    showExternalItemsUpload =
        Core.frostSettings.getBoolValue(SettingsClass.GQ_SHOW_EXTERNAL_ITEMS_UPLOAD);

    if (FcpHandler.inst().getFreenetNode() == null) {
      throw new Exception("No freenet nodes defined");
    }
    final NodeAddress na = FcpHandler.inst().getFreenetNode();
    fcpConn = FcpListenThreadConnection.createInstance(na);
    fcpTools = new FcpMultiRequestConnectionFileTransferTools(fcpConn);

    Core.frostSettings.addPropertyChangeListener(
        new PropertyChangeListener() {
          public void propertyChange(final PropertyChangeEvent evt) {
            if (evt.getPropertyName().equals(SettingsClass.GQ_SHOW_EXTERNAL_ITEMS_DOWNLOAD)) {
              showExternalItemsDownload =
                  Core.frostSettings.getBoolValue(SettingsClass.GQ_SHOW_EXTERNAL_ITEMS_DOWNLOAD);
              if (showExternalItemsDownload) {
                // get external items
                showExternalDownloadItems();
              }
            } else if (evt.getPropertyName().equals(SettingsClass.GQ_SHOW_EXTERNAL_ITEMS_UPLOAD)) {
              showExternalItemsUpload =
                  Core.frostSettings.getBoolValue(SettingsClass.GQ_SHOW_EXTERNAL_ITEMS_UPLOAD);
              if (showExternalItemsUpload) {
                // get external items
                showExternalUploadItems();
              }
            }
          }
        });

    uploadModel = um;
    downloadModel = dm;

    // initially get all items from model
    for (int x = 0; x < uploadModel.getItemCount(); x++) {
      final FrostUploadItem ul = (FrostUploadItem) uploadModel.getItemAt(x);
      if (ul.getGqIdentifier() != null) {
        uploadModelItems.put(ul.getGqIdentifier(), ul);
      }
    }
    for (int x = 0; x < downloadModel.getItemCount(); x++) {
      final FrostDownloadItem ul = (FrostDownloadItem) downloadModel.getItemAt(x);
      if (ul.getGqIdentifier() != null) {
        downloadModelItems.put(ul.getGqIdentifier(), ul);
      }
    }

    // enqueue listeners to keep updated about the model items
    uploadModel.addOrderedModelListener(
        new SortedModelListener<FrostUploadItem>() {
          public void modelCleared() {
            for (final FrostUploadItem ul : uploadModelItems.values()) {
              if (ul.isExternal() == false) {
                fcpTools.removeRequest(ul.getGqIdentifier());
              }
            }
            uploadModelItems.clear();
          }

          public void itemAdded(final int position, final FrostUploadItem item) {
            uploadModelItems.put(item.getGqIdentifier(), item);
            if (!item.isExternal()) {
              // maybe start immediately
              startNewUploads();
            }
          }

          public void itemChanged(final int position, final FrostUploadItem item) {}

          public void itemsRemoved(final int[] positions, final List<FrostUploadItem> items) {
            for (final FrostUploadItem item : items) {
              uploadModelItems.remove(item.getGqIdentifier());
              if (item.isExternal() == false) {
                fcpTools.removeRequest(item.getGqIdentifier());
              }
            }
          }
        });

    downloadModel.addOrderedModelListener(
        new SortedModelListener<FrostDownloadItem>() {
          public void modelCleared() {
            for (final FrostDownloadItem ul : downloadModelItems.values()) {
              if (ul.isExternal() == false) {
                fcpTools.removeRequest(ul.getGqIdentifier());
              }
            }
            downloadModelItems.clear();
          }

          public void itemAdded(final int position, final FrostDownloadItem item) {
            downloadModelItems.put(item.getGqIdentifier(), item);
            if (!item.isExternal()) {
              // maybe start immediately
              startNewDownloads();
            }
          }

          public void itemChanged(final int position, final FrostDownloadItem item) {}

          public void itemsRemoved(final int[] positions, final List<FrostDownloadItem> items) {
            for (final FrostDownloadItem item : items) {
              downloadModelItems.remove(item.getGqIdentifier());
              if (item.isExternal() == false) {
                fcpTools.removeRequest(item.getGqIdentifier());
              }
            }
          }
        });

    directTransferQueue = new DirectTransferQueue();
    directTransferThread = new DirectTransferThread();

    persistentQueue = new FcpPersistentQueue(fcpTools, this);
  }
    @Override
    public void run() {

      final int maxAllowedExceptions = 5;
      int catchedExceptions = 0;

      while (true) {
        try {
          // if there is no work in queue this call waits for a new queue item
          final ModelItem<?> item = directTransferQueue.getItemFromQueue();

          if (item == null) {
            // paranoia, should never happen
            Mixed.wait(5 * 1000);
            continue;
          }

          if (item instanceof FrostUploadItem) {
            // transfer bytes to node
            final FrostUploadItem ulItem = (FrostUploadItem) item;
            // FIXME: provide item, state=Transfer to node, % shows progress
            final String gqid = ulItem.getGqIdentifier();
            final boolean doMime;
            final boolean setTargetFileName;
            if (ulItem.isSharedFile()) {
              doMime = false;
              setTargetFileName = false;
            } else {
              doMime = true;
              setTargetFileName = true;
            }
            final NodeMessage answer =
                fcpTools.startDirectPersistentPut(
                    gqid,
                    ulItem.getFile(),
                    ulItem.getFileName(),
                    doMime,
                    setTargetFileName,
                    ulItem.getCompress(),
                    ulItem.getFreenetCompatibilityMode(),
                    ulItem.getPriority());
            if (answer == null) {
              final String desc = "Could not open a new FCP2 socket for direct put!";
              final FcpResultPut result = new FcpResultPut(FcpResultPut.Error, -1, desc, false);
              FileTransferManager.inst().getUploadManager().notifyUploadFinished(ulItem, result);

              logger.severe(desc);
            } else {
              // wait for an answer, don't start request again
              directPUTsWithoutAnswer.add(gqid);
            }

            directPUTsInProgress.remove(gqid);

          } else if (item instanceof FrostDownloadItem) {
            // transfer bytes from node
            final FrostDownloadItem dlItem = (FrostDownloadItem) item;
            // FIXME: provide item, state=Transfer from node, % shows progress
            final String gqid = dlItem.getGqIdentifier();
            final File targetFile = new File(dlItem.getDownloadFilename());

            final boolean retryNow;
            NodeMessage answer = null;

            try {
              answer = fcpTools.startDirectPersistentGet(gqid, targetFile);
            } catch (final FileNotFoundException e) {
              final String msg =
                  "Could not write to " + dlItem.getDownloadFilename() + ": " + e.getMessage();
              System.out.println(msg);
              logger.severe(msg);
            }

            if (answer != null) {
              final FcpResultGet result = new FcpResultGet(true);
              FileTransferManager.inst()
                  .getDownloadManager()
                  .notifyDownloadFinished(dlItem, result, targetFile);
              retryNow = false;
            } else {
              logger.severe("Could not open a new fcp socket for direct get!");
              final FcpResultGet result = new FcpResultGet(false);
              retryNow =
                  FileTransferManager.inst()
                      .getDownloadManager()
                      .notifyDownloadFinished(dlItem, result, targetFile);
            }

            directGETsInProgress.remove(gqid);

            if (retryNow) {
              startDownload(dlItem);
            }
          }

        } catch (final Throwable t) {
          logger.log(Level.SEVERE, "Exception catched", t);
          catchedExceptions++;
        }

        if (catchedExceptions > maxAllowedExceptions) {
          logger.log(Level.SEVERE, "Stopping DirectTransferThread because of too much exceptions");
          break;
        }
      }
    }
 public boolean isDirectTransferInProgress(final FrostDownloadItem dlItem) {
   final String id = dlItem.getGqIdentifier();
   return directGETsInProgress.contains(id);
 }
 /**
  * @param dlItem items whose global identifier is to check
  * @return true if this item is currently in the global queue, no matter in what state
  */
 public boolean isItemInGlobalQueue(final FrostDownloadItem dlItem) {
   return persistentQueue.isIdInGlobalQueue(dlItem.getGqIdentifier());
 }