Package me.mabra.hellonzb.nntpclient.nioengine

Source Code of me.mabra.hellonzb.nntpclient.nioengine.NettyNioClient$NewTask

/*******************************************************************************
* 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.nntpclient.nioengine;


import me.mabra.hellonzb.HelloNzb;
import me.mabra.hellonzb.HelloNzbCradle;
import me.mabra.hellonzb.parser.DownloadFile;
import me.mabra.hellonzb.parser.DownloadFileSegment;
import me.mabra.hellonzb.util.MyLogger;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.AdaptiveReceiveBufferSizePredictorFactory;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.group.ChannelGroup;
import org.jboss.netty.channel.group.ChannelGroupFuture;
import org.jboss.netty.channel.group.DefaultChannelGroup;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;

import javax.swing.*;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.Vector;
import java.util.concurrent.Executors;


/**
* This is a non-blocking implementation of an NNTP client.
* It is designed as a finite state machine -- see enum
* Channel status and NettyNioClientHandler.java for more
* info.
*
* @author Matthias F. Brandstetter
*/
public class NettyNioClient implements Runnable
{
  private static final int WAIT_AFTER_ERROR = 3;
  private static final int MIN_SLEEP_TIME = 3;

  /** thread's gobal run-flag */
  private boolean runFlag;
 
  /** command this thread to shut down cleanly */
  private boolean shutdown;
 
  /** command this thread to shut down immedeately */
  private boolean shutdownNow;
 
  /** main application object */
  private HelloNzb mainApp;
 
  /** central logger object */
  private MyLogger logger;

  /** Whether or not this NIO client is a backup connection */
  private boolean isBackup;

  /** The host:port combination to connect to */
  private InetAddress serverAddress;
  private int port;
 
  /** "use SSL" flag */
  private boolean useSSL;
 
  /** Username and password */
  private String username;
  private String password;
 
  /** The max. amount of simultaneous connections (download threads) */
  private int threadcount;
 
  /** Authentication test flag */
  private boolean testAuthFlag;
 
  /** Segment availability test flag */
  private boolean testSegAvailability;
 
  /** set as soon as first group cmd was successful */
  private boolean groupCmdSuccessful;
 
  /** The number of threads (connections, sockets) currently in idle status */
  private Integer idleSocketCount;
 
  /** This object is used to handle all SocketChannels and relevant meta information */
  private NettyChannelManager ncMgr;
 
  /** a collection of all open Jetty Channel objects */
  private ChannelGroup channelGroup;
 
  /** the client socket channel factory to use */
  private NioClientSocketChannelFactory clSockChannelFactory;
 
  /** the channel pipeline factory to use */
  private NettyNioClientPipelineFactory channelPipelineFactory;
 
  /** a list of new tasks for this NIO client */
  private Vector<NewTask> newTasks;
     
  /** the last amount of ms to wait for bandwith throttling */
  private Vector<Integer> lastToWaitValues;
 
  /** the amount of bytes downloaded so far */
  private Long downloadedBytes;

  /** For debugging */
  private int chNum = 1;


  /**
   * Class constructor. Makes no connection.
   *
   * @param mainApp Main application object
   * @throws IOException
   * @throws IllegalArgumentException
   */
  public NettyNioClient(HelloNzb mainApp, boolean backup) throws IOException, IllegalArgumentException, UnknownHostException
  {
    // some default values
    this.mainApp = mainApp;
    this.logger = mainApp.getLogger();
    this.runFlag = true;
    this.shutdown = false;
    this.shutdownNow = false;
    this.testAuthFlag = false;
    this.testSegAvailability = false;
    this.groupCmdSuccessful = true;
   
    // usenet server connection settings
    this.isBackup = backup;
    if(backup)
    {
      this.serverAddress = InetAddress.getByName(mainApp.getPrefValue("ServerSettingsBackupHost"));
      this.port = Integer.valueOf(mainApp.getPrefValue("ServerSettingsBackupPort"));
      this.useSSL = mainApp.getBooleanPrefValue("ServerSettingsBackupUseSSL");
      this.threadcount = Integer.parseInt(mainApp.getPrefValue("ServerSettingsBackupThreadCount"));
      this.username = mainApp.getPrefValue("ServerSettingsBackupUsername");
      this.password = mainApp.getPrefValue("ServerSettingsBackupPassword");
    }
    else
    {
      this.serverAddress = InetAddress.getByName(mainApp.getPrefValue("ServerSettingsHost"));
      this.port = Integer.valueOf(mainApp.getPrefValue("ServerSettingsPort"));
      this.useSSL = mainApp.getBooleanPrefValue("ServerSettingsUseSSL");
      this.threadcount = Integer.parseInt(mainApp.getPrefValue("ServerSettingsThreadCount"));
      this.username = mainApp.getPrefValue("ServerSettingsUsername");
      this.password = mainApp.getPrefValue("ServerSettingsPassword");
    }

    this.ncMgr = new NettyChannelManager(mainApp.getPrefContainer());
    this.channelGroup = new DefaultChannelGroup("HelloNzb-Channels");
    this.newTasks = new Vector<>();

    // Netty factories
    long speedLimit = getSpeedLimit();
    if(speedLimit < 0) speedLimit = 0;
    clSockChannelFactory = new NioClientSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool());
    channelPipelineFactory = new NettyNioClientPipelineFactory(this, mainApp.getLocaler(), logger, ncMgr, username, speedLimit, useSSL);
   
    // more default values
    this.idleSocketCount = 0;
    this.lastToWaitValues = new Vector<>();
    this.lastToWaitValues.add(MIN_SLEEP_TIME);
    this.downloadedBytes = 0L;

    printDebugMsg("Initialised.");
  }

  /**
   * Run this thread.
   */
  public void run()
  {
    long currTime = 0;
    long lastTime = 0;

    printDebugMsg("Starting main loop ...");

    // init. socket channel connections
    try
    {
      for(int i = 0; i < threadcount; i++)
      {
        // create new channel
        Channel channel = createNewChannel(10000);
        ncMgr.addChannelAndInit(channel);
        newIdleChannel();
       
        printDebugMsg("> Connecting to server '" + this.serverAddress + "' on port " + this.port + "\r\n", channel);
       
        // update thread view
        updThreadView(channel, mainApp.getLocaler().getBundleText("ThreadViewStatusConnecting"));
      }
    }
    catch(IOException e)
    {
      logger.printStackTrace(e);
      runFlag = false;
    }

    // main loop
    while(runFlag)
    {
      try
      {
        checkShutdown();
        checkTimeout();
       
        // process all open (connected) channels
        for(Channel channel : channelGroup)
          write(channel);
       
        // let the thread sleep a bit
        Thread.sleep(1);
      }
      catch(IOException e)
      {
        logger.printStackTrace(e);
        runFlag = false;
      }
      catch(InterruptedException e)
      {
        runFlag = false;
      }

      if(!isBackup)
      {
        // calculate bytes/sec
        currTime = System.nanoTime();
        if((currTime - lastTime) >= HelloNzbCradle.SEC_MODIFIER)
        {
          // update status bar (count of active threads/connections)
          final int threads = this.threadcount - this.idleSocketCount;
          SwingUtilities.invokeLater(new Runnable()
          {
            public void run()
            {
              // update status bar on main app window
              mainApp.updStatusBar(threads);
            }
          });

          lastTime = currTime;
        }
      }
    }
   
    ChannelGroupFuture groupFuture = channelGroup.close();
    groupFuture.awaitUninterruptibly();
    clSockChannelFactory.releaseExternalResources();
    channelPipelineFactory.releaseExternalResources();
   
    logger.msg("NettyNioClient stopped", MyLogger.SEV_DEBUG);
  }
 
  /**
   * Call this method to close all connections.
   *
   * @param block Block until all connections have been closed
   * @param max Max. value of System.nanoTime() until to wait, -1 for no max.
   */
  public void shutdown(boolean block, long max, boolean forceShutdown)
  {
    logger.msg("NioClient should shutdown now", MyLogger.SEV_DEBUG);
   
    this.shutdown = true;
    this.shutdownNow = forceShutdown;
   
    if(block)
    {
      try
      {
        long curr = System.nanoTime();
        if(max == -1)
          curr = -2;
         
        while(runFlag && curr < max)
        {
          Thread.sleep(10);
          if(max > 0)
            curr = System.nanoTime();
        }
      }
      catch(InterruptedException e)
      {
        // do nothing
      }
    }
  }

  /**
   * Test the specified authentication details (username and password)
   * for correctness.
   *
   * @param handler The RspHandler object to use
   */
  public void testAuth(RspHandler handler)
  {
    // register the response handler
    synchronized(this.newTasks)
    {
      NewTask nt = new NewTask(handler, null, null, null);
      this.newTasks.add(nt);
    }

    // set parameters
    this.testAuthFlag = true;
  }
 
  /**
   * Test whether all segments of the specified DownloadFile exist on server.
   *
   * @param dlFile The DownloadFile to use
   * @param handler The RspHandler to use
   */
  public void testSegAvailability(DownloadFile dlFile, RspHandler handler)
  {
    // register the response handler
    synchronized(this.newTasks)
    {
      NewTask nt = new NewTask(handler, null, null, dlFile);
      this.newTasks.add(nt);
    }
   
    // set parameters
    this.testSegAvailability = true;
  }
 
  /**
   * Returns true if at least one socket (download slot) is available.
   *
   * @return true/false
   */
  public boolean hasFreeSlot()
  {
    synchronized(this.idleSocketCount)
    {
      return this.idleSocketCount >= 1;
    }
  }
 
  /**
   * Fetch the specified usenet article from NNTP server.
   *
   * @param artID The article to fetch, identified by its ID
   * @return true if article could be added to download queue, false if no slots were free
   */
  public boolean fetchArticleData(String group, String artID, RspHandler handler)
  {
    String groupCmd   = "GROUP " + group + "\r\n";
    String articleCmd = "BODY <" + artID + ">\r\n";
   
    synchronized(this.idleSocketCount)
    {
      if(this.idleSocketCount < 1)
        return false;
      else
      {
        this.idleSocketCount--;
        sendNntpCmd(articleCmd, groupCmd, handler);
       
        return true;
      }
    }
  }

  /**
   * Send an array of bytes (a NNTP command) via a SocketChannel.
   *
   * @param cmd The NNTP command to send (in byte[] format)
   * @param group The GROUP command to send (in byte[] format)
   * @param handler The RspHandler object to handle response
   */
  private void sendNntpCmd(String cmd, String group, RspHandler handler)
  {
    // queue the data we want written
    synchronized(this.newTasks)
    {
      NewTask nt = new NewTask(handler, cmd, group, null);
      this.newTasks.add(nt);
    }
  }
 
  /**
   * Check if shutdown flag is set. If so, then command all
   * connections (socket channels) to send the QUIT command
   * next. If the flag is set and all connections have been
   * closed, then set the runFlag to false.
   */
  private void checkShutdown()
  {
    if(shutdown)
    {
      int counter = 0;
      for(Channel channel : channelGroup)
      {
        // let all active channels disconnect
        ChannelStatus status = ncMgr.getNCStatus(channel);
       
        // if shutdownNow flag is true, then shutdown in any socket status
        if(shutdownNow &&   status != ChannelStatus.TO_QUIT &&
                  status != ChannelStatus.FINISHED)
        {
          ncMgr.setNCStatus(channel, ChannelStatus.TO_QUIT);
        }
       
        // if not, then wait until connection is idle (download finished)
        else if(!shutdownNow && status == ChannelStatus.IDLE)
        {
          ncMgr.setNCStatus(channel, ChannelStatus.TO_QUIT);
        }

        counter++;
      }
     
      if(counter == 0)
        runFlag = false; // no active channels left
    }
  }
 
  /**
   * Check if one or more socket channel(s) have not changed
   * its/their status for x seconds (where x is defined by
   * the user via application settings). If a timeout is
   * identified, then the according socket channel is closed
   * and reconnected again.
   */
  private void checkTimeout()
  {
    if(shutdown)
      return;
   
    // loop through all socket channels
    for(int i = 0; i < ncMgr.size(); i++)
    {
      Channel channel = ncMgr.getNC(i);
      long currTime = System.nanoTime();
      long timeout = ncMgr.getNCTimeout(channel);
     
      // downloader paused?
      if(mainApp.isDownloadPaused())
      {
        ncMgr.resetTimeout(channel);
        continue;
      }
     
      // timeout time reached for this socket channel?
      if(currTime > timeout || !channel.isOpen())
      {
        // timeout, so create a new channel...
        try
        {
          // first update thread view
          String msg = mainApp.getLocaler().getBundleText("ThreadViewTimeoutReconnecting");
          updThreadView(channel, msg);

          // close the old channel and wait a second
          if(channel.isConnected())
          {
            ChannelBuffer buffer = ChannelBuffers.wrappedBuffer("QUIT\r\n".getBytes());
            channel.write(buffer);
          }
          channel.close().awaitUninterruptibly();
          Thread.sleep(1000);
         
          // then create new bootstrap (channel helper object)
          Channel newChannel = createNewChannel(5000);
          ncMgr.exchangeNC(channel, newChannel, true);
          ncMgr.setNCStatus(newChannel, ChannelStatus.INIT);
          ncMgr.resetTimeout(newChannel);
        }
        catch(Exception e)
        {
          logger.printStackTrace(e);
        }
      }
    }
  }
 
  /**
   * Write a string to the channel, depending on its current status.
   *
   * @param channel The Netty Channel object to use
   * @throws IOException
   */
  private void write(Channel channel) throws IOException
  {
    // send NNTP command according to current status of this channel
    ChannelStatus status = ncMgr.getNCStatus(channel);
   
    if(status == null)
      return;
   
    if((this.username == null || this.username.length() == 0) && status == ChannelStatus.CONNECTED)
      status = ChannelStatus.IDLE;
   
    switch(status)
    {
      case CONNECTED:
      case W_AUTH_USER:
        // channel has just connected, so send first AUTHINFO command
        ncMgr.setNCStatus(channel, ChannelStatus.R_AUTH_USER);
        writeStatusAuthinfo("AUTHINFO USER " + this.username + "\r\n", channel);
        break;
       
      case W_AUTH_PASS:
        // first AUTHINFO command sent, now send the second part
        ncMgr.setNCStatus(channel, ChannelStatus.R_AUTH_PASS);
        writeStatusAuthinfo("AUTHINFO PASS " + this.password + "\r\n", channel);
        break;
       
      case IDLE:
        handleIdleState(channel);
        break;
   
      case START_FETCH:
        // authentication successful, after IDLE state, now do something
        if(!this.groupCmdSuccessful)
        {
          ncMgr.setNCStatus(channel, ChannelStatus.GROUP_SENT);
          writeStatusCmds(channel, false);
        }
        else
        {
          ncMgr.setNCStatus(channel, ChannelStatus.READY);
        }
        break;
   
      case READY:
        // GROUP command successful, so send article fetch command now
        ncMgr.setNCStatus(channel, ChannelStatus.START_RECEIVE);
        writeStatusCmds(channel, true);
        break;

      case SERVER_ERROR:
        handleServerErrorState(channel);
        break;

      case TO_QUIT:
        // article has been fetched, so send QUIT command
        ncMgr.setNCStatus(channel, ChannelStatus.FINISHED);
        writeStatusArticleFetched(channel);
        break;
    }
  }
 
  /**
   * Handle state "SERVER_ERROR".
   *
   * @param channel The channel to use
   */
  private void handleServerErrorState(Channel channel)
  {
    RspHandler handler = null;
   
    if(this.testSegAvailability)
    {
      // segment availability check mode
      this.shutdown = true;
      handler = ncMgr.getRspHandler(channel);
      if(handler != null)
        handler.setFinished();
    }
   
    if(shutdown)
    {
      ncMgr.setNCStatus(channel, ChannelStatus.TO_QUIT);
      return;
    }
   
    // 481 exceeded maximum number of connections per user
    if(!ncMgr.containsErrorWait(channel))
    {
      // calculate time until socket has to wait for (10 sec)
      long fin = System.nanoTime() + (HelloNzbCradle.SEC_MODIFIER * WAIT_AFTER_ERROR);
      ncMgr.setErrorWait(channel, fin);
    }
    else
    {
      // waiting done?
      long fin = ncMgr.getErrorWait(channel);
      if(System.nanoTime() >= fin)
      {
        ncMgr.removeErrorWait(channel);
        ncMgr.setNCStatus(channel, ChannelStatus.INIT);
      }
    }
  }

  /**
   * Handle state "IDLE".
   *
   * @param channel The channel to use
   */
  private void handleIdleState(Channel channel)
  {
    boolean empty = true;
    RspHandler handler = null;
   
   
    // first check if there is already a RspHandler object registered
    // for this socket channel. If so then it's likely that we have
    // reconnected after a timeout
    ArticleMetadata md = ncMgr.getMD(channel);
    handler = ncMgr.getRspHandler(channel);
    if(md != null && handler != null)
    {
      String cmd = md.cmd;
      String group = md.group;
      NewTask nt = new NewTask(handler, cmd, group, null);
   
      synchronized(this.newTasks)
      {
        newTasks.add(0, nt);
      }
    }
   
    // new tasks to process?
    synchronized(this.newTasks)
    {
      if(!newTasks.isEmpty())
        empty = false;
    }

    if(this.testAuthFlag)
    {
      if(!empty)
      {
        // authentication check mode, channel already authenticated and waiting
        handler = null;
        synchronized(this.newTasks)
        {
          handler = newTasks.firstElement().handler;
          newTasks.remove(0);
        }
        handler.setError(RspHandler.ERR_NONE, ""); // authentication test succeeded
        handler.setFinished();
        ncMgr.setNCStatus(channel, ChannelStatus.TO_QUIT);
        shutdown = true;
      }
    }
    else if(this.testSegAvailability)
    {
      if(!empty)
      {
        // test segment availability mode, channel already authenticated and waiting
        handler = null;
        DownloadFile dl = null;
        synchronized(this.newTasks)
        {
          handler = newTasks.firstElement().handler;
          dl = newTasks.firstElement().dl;
          newTasks.remove(0);
        }
       
        ncMgr.setRspHandler(channel, handler);
        ncMgr.setDLFile(channel, dl);
        ncMgr.setDLFileSeg(channel, dl.getAllOriginalSegments());
        ncMgr.setNCStatus(channel, ChannelStatus.START_FETCH);
      }
    }
    else
    {
      // check if new tasks are available
      if(!empty)
      {
        NewTask task = null;
        synchronized(this.newTasks)
        {
          // yes, so get the first task in queue...
          task = newTasks.firstElement();
          newTasks.remove(0);
        }

        // ...and prepare it for further processing
        ArticleMetadata articleData = new ArticleMetadata(task.cmd, task.group);
        ncMgr.setMD(channel, articleData);
        ncMgr.setRspHandler(channel, task.handler);
        ncMgr.setNCStatus(channel, ChannelStatus.START_FETCH);
      }
    }
  }
 
  /**
   * Helper method for the write() method.
   *
   * @param cmd The command to send
   * @param channel The socket channel to use
   * @throws IOException
   */
  private void writeStatusAuthinfo(String cmd, Channel channel) throws IOException
  {
    printDebugMsg("> " + cmd, channel);
   
    ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(cmd.getBytes());
    channel.write(buffer);
  }
 
  /**
   * Helper method for the write() method.
   *
   * @param channel The socket channel to use
   * @param fetchFlag True when the fetch command should be send, false for the GROUP cmd
   * @return Whether or not the thread should be switched to READ mode
   * @throws IOException
   */
  private boolean writeStatusCmds(Channel channel, boolean fetchFlag) throws IOException
  {
    ArticleMetadata metadata = ncMgr.getMD(channel);
    String string = null;
   
    if(!this.testSegAvailability)
    {
      // normal download activity
      if(fetchFlag)
      {
        // send fetch command
        string = metadata.cmd;
       
        // update thread view
        String cmd = new String(metadata.cmd);
        String msg = mainApp.getLocaler().getBundleText("ThreadViewStatusFetchArticle") + " " + cmd.substring(5, cmd.length() - 2);
        updThreadView(channel, msg);
      }
      else
        string = metadata.group; // send GROUP command
    }
    else
    {
      // test download file segment availability
      if(fetchFlag)
      {
        Vector<DownloadFileSegment> segs = ncMgr.getDLFileSeg(channel);
       
        if(segs.isEmpty())
        {
          // no more segments left to test
          RspHandler handler = ncMgr.getRspHandler(channel);
          handler.setFinished();
          ncMgr.setNCStatus(channel, ChannelStatus.IDLE);
          ncMgr.removeRspHandler(channel);
          ncMgr.removeDLFile(channel);
          ncMgr.removeDLFileSeg(channel);
         
          // update thread view
          updThreadView(channel, mainApp.getLocaler().getBundleText("ThreadViewStatusConnected"));
         
          return false;
        }
        else
        {
          // get next article to test
          String article = segs.firstElement().getArticleId();
          String cmd = "HEAD <" + article + ">\r\n";
          string = cmd;
          segs.remove(0);
          ncMgr.setDLFileSeg(channel, segs);
         
          // update thread view
          String msg = mainApp.getLocaler().getBundleText("ThreadViewStatusCheckArticle") + " <" + article + ">";
          updThreadView(channel, msg);
        }
      }
      else
      {
        DownloadFile dl = ncMgr.getDLFile(channel);
        String group = "GROUP " + dl.getGroups().firstElement() + "\r\n";
        string = group;
      }
    }
     
    printDebugMsg("> " + string, channel);

    // write out data to channel
    ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(string.getBytes());
    channel.write(buffer);
   
    return true;
  }
 
  /**
   * Helper method for the write() method.
   *
   * @param channel The socket channel to use
   * @throws IOException
   */
  private void writeStatusArticleFetched(Channel channel) throws IOException
  {
    String cmd = "QUIT\r\n";

    // update thread view
    updThreadView(channel, mainApp.getLocaler().getBundleText("ThreadViewStatusDisconnecting"));
    printDebugMsg("> " + cmd, channel);

    // send QUIT command to remote server
    ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(cmd.getBytes());
    channel.write(buffer);
  }

  /**
   * Create a new Jetty-based Channel. The parameter specifies how long
   * to wait during connection attempt before timeout.
   *
   * @param timeoutMillis The amount of time to wait in milli seconds
   * @return The newly created Channel object
   * @throws IOException If no connection could be established
   */
  private Channel createNewChannel(long timeoutMillis) throws IOException
  {
    logger.msg("Creating new channel #" + chNum++, MyLogger.SEV_DEBUG);

    // configure the Netty client
    ClientBootstrap bootstrap = new ClientBootstrap(clSockChannelFactory);
   
    // configure the pipeline factory
    bootstrap.setPipelineFactory(channelPipelineFactory);
    bootstrap.setOption("tcpNoDelay", true);
    bootstrap.setOption("keepAlive", true);
    bootstrap.setOption("child.receiveBufferSizePredictorFactory",
        new AdaptiveReceiveBufferSizePredictorFactory());

    // start the connection attempt
    ChannelFuture future = bootstrap.connect(new InetSocketAddress(serverAddress, port));
    Channel channel = null;
    if(future.awaitUninterruptibly(timeoutMillis))
      channel = future.getChannel();
    else
      throw new IOException("Could not connect to remote server");

    if(!channel.isConnected())
    {
      try
      {
        logger.msg("New channel could not connect to remote host", MyLogger.SEV_WARNING);
        Thread.sleep(timeoutMillis);
      }
      catch(InterruptedException e) {}
    }

    return channel;
  }
 
  /**
   * Handle a new idle socket channel.
   */
  protected void newIdleChannel()
  {
    synchronized(idleSocketCount)
    {
      idleSocketCount++;
    }
  }
 
  /**
   * Called from the NettyNioClientHandler when the auth test was unsuccessful.
   */
  protected void authTestError()
  {
    // authentication check mode, channel already authenticated and waiting
    RspHandler handler = null;
    synchronized(this.newTasks)
    {
      handler = newTasks.firstElement().handler;
      newTasks.remove(0);
    }

    handler.setError(RspHandler.ERR_AUTH, "could not authenticate"); // authentication test failed
    handler.setFinished();
  }
 
  /**
   * Add an amount of bytes to the count of totally downloaded bytes.
   *
   * @param bytes The value to add
   */
  protected void addToDownloadedBytes(long bytes)
  {
    synchronized(downloadedBytes)
    {
      downloadedBytes += bytes;
    }
  }

  /**
   * Prints a debug message to standard output.
   * Prints the connection type (default/backup) in front of every line.
   *
   * @param msg The message to print
   */
  protected void printDebugMsg(String msg)
  {
    String type = isBackup ? "Backup" : "Default";
    logger.msg(type + " NIO client: " + msg, MyLogger.SEV_DEBUG);
  }

  /**
   * Prints a debug message to standard output.
   * Prints the thread/connection number in front of every line.
   * If null passed as socket channel object, then no thread nr. is printed.
   *
   * @param msg The message to print
   * @param channel The socket channel / thread / connection number (counted from 0)
   */
  protected void printDebugMsg(String msg, Channel channel)
  {
    if(msg == null)
      return// don't remove me!

    // message has more than one line
    if(msg.indexOf("\r\n") != msg.lastIndexOf("\r\n"))
      msg = "[" + msg.length() + " bytes received from server]\r\n";

    if(channel != null)
      msg = (ncMgr.indexOfNC(channel) + 1) + "| " + msg;
    else
      msg = "?| " + msg;

    logger.msg(msg, MyLogger.SEV_DEBUG);
  }

  /**
   * Helper method to update thread view in main window.
   *
   * @param channel The channel to update
   * @param t New text to display
   */
  protected void updThreadView(Channel channel, String t)
  {
    if(channel == null || isBackup)
      return;
   
    final int tnum = ncMgr.indexOfNC(channel);
    final String text = t;
   
    if(tnum < 0)
      return;
 
        SwingUtilities.invokeLater(new Runnable()
        {
          public void run()
          {
            // update thread view table
            mainApp.updThreadView(tnum, text);
          }
        } );
  }

  /**
   * Get the download speed limit set by the user.
   *
   * @return The speed limit in KB/s, or -1 if not set
   */
  protected long getSpeedLimit()
  {
    // get speed limit value from preferences
    long limit = 0;
    try
    {
      limit = Integer.valueOf(mainApp.getPrefValue("DownloadSettingsMaxConnectionSpeed"));
    }
    catch(NumberFormatException e)
    {
      return -1L;
    }
   
    return limit;
  }
 
  /**
   * Set the global download speed limit.
   *
   * @param limit The limit to set (in KB/s)
   */
  public void setSpeedLimit(long limit)
  {
    channelPipelineFactory.setDlSpeedLimit(limit);
  }
   
  /**
   * Returns the current number of read bytes since the last check interval
   * (check interval = 1000ms per default).
   *
   * @return The number of read bytes
   */
  public long getDlTraffic()
  {
    return channelPipelineFactory.getDlTraffic();
  }
 
  protected void setGroupCmdSuccessful(boolean value)
  {
    groupCmdSuccessful = value;
  }
 
  protected boolean isGroupCmdSuccessful()
  {
    return groupCmdSuccessful;
  }
 
  protected boolean isTestAuthFlag()
  {
    return testAuthFlag;
  }

  protected boolean isTestSegAvailability()
  {
    return testSegAvailability;
  }

  protected ChannelGroup getChannelGroup()
  {
    return channelGroup;
  }
 
 
  class NewTask
  {
    public RspHandler handler;
    public String cmd;
    public String group;
    public DownloadFile dl;
   
    public NewTask(RspHandler h, String c, String g, DownloadFile dl)
    {
      this.handler = h;
      this.cmd = c;
      this.group = g;
      this.dl = dl;
    }
  }
}

































TOP

Related Classes of me.mabra.hellonzb.nntpclient.nioengine.NettyNioClient$NewTask

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.