Package me.mabra.hellonzb.util

Source Code of me.mabra.hellonzb.util.BackgroundWorker

/*******************************************************************************
* HelloNzb -- The Binary Usenet Tool
* Copyright (C) 2010-2013 Matthias F. Brandstetter
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package me.mabra.hellonzb.util;

import me.mabra.hellonzb.HelloNzb;
import me.mabra.hellonzb.HelloNzbCradle;
import me.mabra.hellonzb.HelloNzbToolkit;
import me.mabra.hellonzb.listener.actions.TimedDownloadsAction;
import me.mabra.hellonzb.parser.NzbParser;

import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.Vector;


/**
* This class performs multiple different background tasks during HelloNzb
* program execution:
*  - check for new files to download (from other program instances)
*  - check for new files to download (from auto-load directory)
*  - update speed graph & misc. GUI elements
*  - update tray icon animation
* This class should be used as a thread, it runs in background performing
* its tasks once per second.
* @author Matthias F. Brandstetter
*/
public class BackgroundWorker extends Thread
  private static final int BPS_HISTORY_SIZE = 5;

  private HelloNzbCradle mainApp;
  private MyLogger logger;
  private int bufferSize;
  private RandomAccessFile raf;
  private MappedByteBuffer mbb;
  private FixedSizeQueue<Long> bpsHistory;
  private boolean shutdown;
  private StringLocaler localer;
 
 
  /**
   * Class constructor.
   *
   * @param mainApp The main application object
   * @param bufferSize The buffer size to use for instance communication
   * @throws IOException
   */
  public BackgroundWorker(HelloNzbCradle mainApp, int bufferSize) throws IOException
  {
    this.mainApp = mainApp;
    this.logger = mainApp.getLogger();
    this.bufferSize = bufferSize;
    this.shutdown = false;

    this.localer = mainApp.getLocaler();
   
    String tempDir = System.getProperty("java.io.tmpdir");
    String mapFile = tempDir + File.separator + "HelloNzb-memMap";
   
    File f = new File(mapFile);
    if(f.exists())
      f.delete();
   
    raf = new RandomAccessFile(mapFile, "rw");
    mbb = raf.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, bufferSize);
    bpsHistory = new FixedSizeQueue<>(BPS_HISTORY_SIZE);
  }
 
  /**
   * Called via thread.start()
   */
  public void run()
  {
    SpeedGraphPanel speedGraph = null;
   
    while(!shutdown)
    {
      try
      {
        // check if another program instance told us something
        checkCommFromInstance();
       
        // check new files in auto-load directory
        checkAutoLoadDir();

        // check whether we need to auto-start or -stop the download because of timed downloads
        try
        {
          checkTimedDownloads();
        }
        catch(Exception ex) {}

        // get total size of all NZB files in queue
        long totalBytesInQueue = totalBytesInQueue();
       
        // update main app's ETA and total file size label
        long lastBps = updateEtaLabels(totalBytesInQueue);

        // update memory usage label
        updateMemoryUsageLabel();

        // update speed history graph
        if(speedGraph == null)
          speedGraph = mainApp.getSpeedGraphPanel();
        else
        {
          speedGraph.add(lastBps);
          speedGraph.repaint();
        }
       
        // let the thread sleep a bit
        try
        {
          Thread.sleep(1000);
        }
        catch(InterruptedException e) {}
      }
      catch(Exception e)
      {
        logger.printStackTrace(e);
      }
    }
   
    // cleanup
    try
    {
      raf.close();
    }
    catch(IOException e)
    {
      logger.printStackTrace(e);
    }
  }
 
  /**
   * Called in order to shutdown this thread.
   */
  public void shutdown()
  {
    shutdown = true;
  }

  /**
   * Determine the currently used memory, then update application status bar accordingly.
   */
  private void updateMemoryUsageLabel()
  {
    Runtime runtime = Runtime.getRuntime();
    String mem = " " + HelloNzbToolkit.prettyPrintFilesize(runtime.totalMemory());
    mainApp.setMemoryUsageLabel(localer.getBundleText("StatusBarMemoryUsage") + mem);
  }

  /**
   * Called to update the ETA and related lables on main window.
   *
   * @param totalBytesInQueue The last total amount of bytes value
   * @return The newly calculated lastBps value
   */
  private long updateEtaLabels(long totalBytesInQueue)
  {
    long etaSecs = 0;
    long lastBps = 0;
    long origLastBps = ((HelloNzb) mainApp).lastBpsValue();
   
    // calculate avg. value of bps history
    bpsHistory.add(origLastBps);
    int i = 0;
    for(; i < bpsHistory.size(); i++)
      lastBps += bpsHistory.get(i);
    lastBps /= i;
    ((HelloNzb) mainApp).setCurrDlSpeedLabel(lastBps);
   
    // update GUI
    if(lastBps > 0)
      etaSecs = (long) (totalBytesInQueue / lastBps);
    if(totalBytesInQueue == 0)
      mainApp.setEtaAndTotalLabel("");
    else
    {
      String sizeString = HelloNzbToolkit.prettyPrintFilesize(totalBytesInQueue);
      String etaString = HelloNzbToolkit.prettyPrintSeconds(etaSecs);
      mainApp.setEtaAndTotalLabel(sizeString + " / " + etaString);
    }
   
    return lastBps;
  }
 
  /**
   * Called to calculate the toal amount of bytes that is currently
   * in the download queue.
   *
   * @return The calculated total
   */
  private long totalBytesInQueue()
  {
    long totalBytesInQueue = 0L;
   
    Vector<NzbParser> parsers = mainApp.getNzbQueue();
    for(int i = 0; i < parsers.size(); i++)
      totalBytesInQueue += parsers.get(i).getCurrTotalSize();
   
    return totalBytesInQueue;
  }
 
  /**
   * Check if another program instance told us something.
   */
  private void checkCommFromInstance()
  {
    byte [] data = new byte[bufferSize];
    String dataString = null;
   
    try
    {
      data[0] = 0;
      mbb.rewind();

      // any bytes to read from the mapping buffer?
      int bytecnt = mbb.remaining();
      if(bytecnt > bufferSize)
        bytecnt = bufferSize;

      // new data to read?
      if(bytecnt > 0)
      {
        mbb.get(data, 0, bytecnt);
       
        if(data[0] != 0)
        {
          int i = 0;
          while(i < data.length && data[i] != 0)
            i++;
         
          dataString = new String(data);
          dataString = dataString.substring(0, i);
         
          // parse command
          if(dataString.length() >= 3)
          {
            if(dataString.substring(0, 3).equals("NZB"))
            {
              // new NZB file to load
              final String filename = dataString.substring(4, dataString.length());
              final NzbParser parser = new NzbParser(filename, mainApp.getPrefValue("GeneralSettingsDownloadDir"));
              logger.msg("Loading new NZB file from external instance: " + filename, MyLogger.SEV_INFO);
              EventQueue.invokeLater(new Runnable()
              {
                public void run()
                {
                  mainApp.addNzbToQueue(parser);
                }
              });
            }
            else if(dataString.substring(0, 4).equals("SHOW"))
            {
              // show main window of running instance
              logger.msg("Second instance started, show window of running instance.", MyLogger.SEV_DEBUG);
              EventQueue.invokeLater(new Runnable()
              {
                public void run()
                {
                  mainApp.getJFrame().setVisible(true);
                }
              });
            }
            else
              throw new Exception("invalid command received from external instance (" + dataString + ")");
          }
          else
            throw new Exception("invalid command received from external instance (" + dataString + ")");
        }
      }
    }
    catch(Exception e)
    {
      logger.printStackTrace(e);
    }
    finally
    {
      mbb.clear();
      mbb.put(new byte[] { 0 });
    }
  }

  /**
   * Check for new files in auto-load directory.
   */
  private void checkAutoLoadDir()
  {
    final String pref = mainApp.getPrefValue("GeneralSettingsAutoLoadDir");
    if(pref.isEmpty())
      return;
   
    final File autoLoadDir = new File(pref);
    if(!autoLoadDir.isDirectory() || !autoLoadDir.canRead() || !autoLoadDir.canExecute())
      return;

    // directory exists, so load all nzb files from it
    boolean okToDel = false;
    File [] files = autoLoadDir.listFiles();
    for(File file : files)
    {
      okToDel = false;
     
      try
      {
        // directory
        if(file.isDirectory())
          continue;
       
        // nzb file?
        String filename = file.getCanonicalPath();
        if(!filename.substring(filename.length() - 4, filename.length()).equalsIgnoreCase(".nzb"))
          continue;
       
        // yes, so add it to the download queue
        final NzbParser newParser = new NzbParser(file.getAbsolutePath(), mainApp.getPrefValue("GeneralSettingsDownloadDir"));
        logger.msg("Loading new NZB file from auto-load directory: " + filename, MyLogger.SEV_INFO);
        EventQueue.invokeLater(new Runnable()
        {
          public void run()
          {
            mainApp.addNzbToQueue(newParser);
            if(mainApp.getDownloadFileCount() > 0 && !mainApp.isDownloadActive())
              mainApp.startDownload(); // start download if not already running
          }
        });
        okToDel = true;
      }
      catch(Exception e)
      {
        logger.msg("Failed to load NZB file from auto-load directory", MyLogger.SEV_ERROR);
        logger.printStackTrace(e);
      }
      finally
      {
        if(okToDel && file != null && file.canWrite())
          file.delete();
      }
    }
  }

  /**
   * Check whether we need to auto-start or -stop the download because of timed downloads.
   */
  private void checkTimedDownloads() throws ParseException
  {
    // check whether user wants timed downloads (preferences)
    String startTime = mainApp.getPrefValue("DownloadSettingsTimedDownloadStart");
    if(startTime == null || startTime.isEmpty())
      return;
    String stopTime = mainApp.getPrefValue("DownloadSettingsTimedDownloadStop");
    if(stopTime == null || stopTime.isEmpty())
      return;

    // are timed downloads enabled in the menu?
    if(!((TimedDownloadsAction) mainApp.getMenuToolbarAction("MenuDownloadTimedDownloads")).isSelected())
      return;

    // yes, so calculate from and to times
    String [] startTimes = startTime.split(":");
    String [] stopTimes = stopTime.split(":");
    int from = Integer.parseInt(startTimes[0]) * 100 + Integer.parseInt(startTimes[1]);
    int to = Integer.parseInt(stopTimes[0]) * 100 + Integer.parseInt(stopTimes[1]);

    // compare current time with given from and to times
    Date date = new Date();
    Calendar c = Calendar.getInstance();
    c.setTime(date);
    int t = c.get(Calendar.HOUR_OF_DAY) * 100 + c.get(Calendar.MINUTE);
    if(to > from && t >= from && t <= to || to < from && (t >= from || t <= to))
    {
      // within time frame, start download
      if(!mainApp.isDownloadActive() && mainApp.getDownloadFileCount() > 0)
      {
        logger.msg("Auto-starting download based on times set in preferences", MyLogger.SEV_INFO);
        startStopDl();
      }
    }
    else
    {
      // outside time frame, stop download
      if(mainApp.isDownloadActive())
      {
        startStopDl();
        logger.msg("Auto-stopping download based on times set in preferences", MyLogger.SEV_INFO);
      }
    }
  }

  private void startStopDl()
  {
    EventQueue.invokeLater(new Runnable()
    {
      public void run()
      {
        mainApp.startDownload();
      }
    });
  }
}
































TOP

Related Classes of me.mabra.hellonzb.util.BackgroundWorker

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.