001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.plugins.streetside.io.export;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.image.BufferedImage;
007import java.io.IOException;
008import java.util.HashSet;
009import java.util.Set;
010import java.util.concurrent.ArrayBlockingQueue;
011import java.util.concurrent.ThreadPoolExecutor;
012import java.util.concurrent.TimeUnit;
013
014import org.apache.log4j.Logger;
015import org.openstreetmap.josm.gui.PleaseWaitRunnable;
016import org.openstreetmap.josm.gui.progress.swing.PleaseWaitProgressMonitor;
017import org.openstreetmap.josm.plugins.streetside.StreetsideAbstractImage;
018import org.openstreetmap.josm.plugins.streetside.StreetsideImage;
019
020/**
021 * Export main thread. Exportation works by creating a
022 * {@link StreetsideExportWriterThread} and several
023 * {@link StreetsideExportDownloadThread}. The second ones download every single
024 * image that is going to be exported and stores them in an
025 * {@link ArrayBlockingQueue}. Then it is picked by the first one and written on
026 * the selected folder. Each image will be named by its key.
027 *
028 * @author nokutu
029 * @see StreetsideExportWriterThread
030 * @see StreetsideExportDownloadThread
031 */
032public class StreetsideExportManager extends PleaseWaitRunnable {
033
034  final static Logger logger = Logger.getLogger(StreetsideExportManager.class);
035
036  private final ArrayBlockingQueue<BufferedImage> queue = new ArrayBlockingQueue<>(10);
037  private final ArrayBlockingQueue<StreetsideAbstractImage> queueImages = new ArrayBlockingQueue<>(10);
038
039  private int amount;
040  private Set<StreetsideAbstractImage> images;
041  private String path;
042
043  private Thread writer;
044  private ThreadPoolExecutor ex;
045
046  /**
047   * Main constructor.
048   *
049   * @param images Set of {@link StreetsideAbstractImage} objects to be exported.
050   * @param path Export path.
051   */
052  public StreetsideExportManager(Set<StreetsideAbstractImage> images, String path) {
053    super(
054      tr("Downloading") + "…",
055      new PleaseWaitProgressMonitor(tr("Exporting Streetside Images")),
056      true
057    );
058    this.images = images == null ? new HashSet<>() : images;
059    this.path = path;
060    amount = this.images.size();
061  }
062
063  @Override
064  protected void cancel() {
065    writer.interrupt();
066    ex.shutdown();
067  }
068
069  @Override
070  protected void realRun() throws IOException {
071    // Starts a writer thread in order to write the pictures on the disk.
072    writer = new StreetsideExportWriterThread(path, queue,
073        queueImages, amount, getProgressMonitor());
074    writer.start();
075    if (path == null) {
076      try {
077        writer.join();
078      } catch (InterruptedException e) {
079        logger.error(e);
080      }
081      return;
082    }
083    ex = new ThreadPoolExecutor(20, 35, 25, TimeUnit.SECONDS,
084      new ArrayBlockingQueue<>(10));
085    for (StreetsideAbstractImage image : images) {
086      if (image instanceof StreetsideImage) {
087        try {
088          ex.execute(new StreetsideExportDownloadThread(
089              (StreetsideImage) image, queue, queueImages));
090        } catch (Exception e) {
091          logger.error(e);
092        }
093      }
094      try {
095        // If the queue is full, waits for it to have more space
096        // available before executing anything else.
097        while (ex.getQueue().remainingCapacity() == 0) {
098          Thread.sleep(100);
099        }
100      } catch (Exception e) {
101        logger.error(e);
102      }
103    }
104    try {
105      writer.join();
106    } catch (InterruptedException e) {
107      logger.error(e);
108    }
109  }
110
111  @Override
112  protected void finish() {
113  }
114}