/** Check that all pools have a priority. */
 private void checkPoolPriorities(List<Pool> pools) {
   for (Pool pool : pools) {
     if (pool.getPriority() == null) {
       pool.setPriority(getMinimumPoolPriority() + 1);
     }
   }
 }
  /**
   * Compute the and set the current pool. Based on the pool priority and pool state.
   *
   * @param connection
   * @return
   */
  @Override
  protected void computeCurrentPool() throws NoPoolAvailableException {
    List<Pool> pools = getProxyManager().getPools();
    Pool newPool = null;
    checkPoolPriorities(pools);
    Collections.sort(
        pools,
        new Comparator<Pool>() {
          public int compare(Pool o1, Pool o2) {
            return o1.getPriority().compareTo(o2.getPriority());
          }
        });
    for (Pool pool : pools) {
      if (pool.isReady() && pool.isEnabled() && pool.isStable()) {
        newPool = pool;
        break;
      }
    }

    if (newPool == null) {
      throw new NoPoolAvailableException("No pool available. " + pools);
    } else {
      setCurrentPool(newPool);
    }
  }
 /** Stop all pools */
 public void stopPools() {
   synchronized (pools) {
     for (Pool pool : pools) {
       pool.stopPool("Proxy is shutting down!");
     }
   }
 }
  /**
   * Called when a pool send a notify request.
   *
   * @param pool
   * @param setDifficulty
   */
  public void onPoolNotify(Pool pool, MiningNotifyNotification notify) {
    if (notify.getCleanJobs()) {
      LOGGER.info("New block detected on pool {}.", pool.getName());
    }

    MiningNotifyNotification notification = new MiningNotifyNotification();
    notification.setBitcoinVersion(notify.getBitcoinVersion());
    notification.setCleanJobs(notify.getCleanJobs());
    notification.setCoinbase1(notify.getCoinbase1());
    notification.setCoinbase2(notify.getCoinbase2());
    notification.setCurrentNTime(notify.getCurrentNTime());
    notification.setJobId(notify.getJobId());
    notification.setMerkleBranches(notify.getMerkleBranches());
    notification.setNetworkDifficultyBits(notify.getNetworkDifficultyBits());
    notification.setPreviousHash(notify.getPreviousHash());

    Set<WorkerConnection> connections = getPoolWorkerConnections(pool);

    if (connections == null || connections.isEmpty()) {
      LOGGER.debug("No worker connections on pool {}. Do not send notify.", pool.getName());
    } else {
      for (WorkerConnection connection : connections) {
        connection.onPoolNotify(notification);
      }
    }
  }
 /** Called by pool when its state changes */
 public void onPoolStateChange(Pool pool) {
   if (pool.isReady()) {
     LOGGER.warn("Pool {} is UP.", pool.getName());
     poolSwitchingStrategyManager.onPoolUp(pool);
   } else {
     LOGGER.warn("Pool {} is DOWN. Moving connections to another one.", pool.getName());
     poolSwitchingStrategyManager.onPoolDown(pool);
   }
 }
 /**
  * Return the minimal priority over all pools.
  *
  * @param addPoolDTO
  * @return
  */
 private int getMinimumPoolPriority() {
   int minPriority = 0;
   List<Pool> pools = getProxyManager().getPools();
   for (Pool pool : pools) {
     if (pool.getPriority() > minPriority) {
       minPriority = pool.getPriority();
     }
   }
   return minPriority;
 }
 /**
  * Return the pool based on the pool name.
  *
  * @param poolHost
  * @return
  */
 public Pool getPool(String poolName) {
   Pool result = null;
   synchronized (pools) {
     for (Pool pool : pools) {
       if (pool.getName().toString().equals(poolName)) {
         result = pool;
         break;
       }
     }
   }
   return result;
 }
  /**
   * Disable/Enable the pool with the given name
   *
   * @param poolName
   * @param isEnabled
   * @throws NoPoolAvailableException
   */
  public void setPoolEnabled(String poolName, boolean isEnabled)
      throws NoPoolAvailableException, Exception {
    Pool pool = getPool(poolName);
    if (pool == null) {
      throw new NoPoolAvailableException("Pool with name " + poolName + " is not found");
    }

    if (pool.isEnabled() != isEnabled) {
      LOGGER.info("Set pool {} {}", pool.getName(), isEnabled ? "enabled" : "disabled");
      pool.setEnabled(isEnabled, this);
    }
  }
  @Override
  public void onPoolAdded(Pool pool) {
    // If the priority of the pool is set, check if it does not overlap
    // another pool with the same priority
    if (pool.getPriority() == null) {
      // Set by default the priority to the lowest over all pools.
      int minPriority = getMinimumPoolPriority();
      pool.setPriority(minPriority + 1);
    } else {
      // If the priority is set, update the other pools.
      onPoolUpdated(pool);
    }

    super.onPoolAdded(pool);
  }
  /**
   * To call when a subscribe request is received on a worker connection. Return the pool on which
   * the connection is bound.
   *
   * @param connection
   * @param request
   */
  public Pool onSubscribeRequest(WorkerConnection connection, MiningSubscribeRequest request)
      throws NoPoolAvailableException {
    Pool pool = poolSwitchingStrategyManager.getPoolForConnection(connection);

    Set<WorkerConnection> workerConnections = getPoolWorkerConnections(pool);
    workerConnections.add(connection);
    this.workerConnections.add(connection);
    LOGGER.info(
        "New WorkerConnection {} subscribed. {} connections active on pool {}.",
        connection.getConnectionName(),
        workerConnections.size(),
        pool.getName());

    return pool;
  }
 /** Start all pools. */
 public void startPools(List<Pool> pools) {
   this.pools = Collections.synchronizedList(new ArrayList<Pool>(pools));
   synchronized (pools) {
     for (Pool pool : pools) {
       try {
         if (pool.isEnabled()) {
           pool.startPool(this);
         } else {
           LOGGER.warn("Do not start pool {} since it is disabled.", pool.getName());
         }
       } catch (Exception e) {
         LOGGER.error("Failed to start the pool {}.", pool, e);
       }
     }
   }
 }
  /**
   * Called when a pool set the difficulty.
   *
   * @param pool
   * @param setDifficulty
   */
  public void onPoolSetDifficulty(Pool pool, MiningSetDifficultyNotification setDifficulty) {
    LOGGER.info("Set difficulty {} on pool {}.", setDifficulty.getDifficulty(), pool.getName());

    MiningSetDifficultyNotification notification = new MiningSetDifficultyNotification();
    notification.setDifficulty(setDifficulty.getDifficulty());

    Set<WorkerConnection> connections = getPoolWorkerConnections(pool);

    if (connections == null || connections.isEmpty()) {
      LOGGER.debug("No worker connections on pool {}. Do not send setDifficulty.", pool.getName());
    } else {
      for (WorkerConnection connection : connections) {
        connection.onPoolDifficultyChanged(notification);
      }
    }
  }
  /**
   * Switch the given connection to the given pool.
   *
   * @param connection
   * @param newPool
   */
  public void switchPoolForConnection(WorkerConnection connection, Pool newPool)
      throws TooManyWorkersException, ChangeExtranonceNotSupportedException {
    // If the old pool is the same as the new pool, do nothing.
    if (!newPool.equals(connection.getPool())) {
      // Remove the connection from the old pool connection list.
      Set<WorkerConnection> oldPoolConnections = getPoolWorkerConnections(connection.getPool());
      if (oldPoolConnections != null) {
        oldPoolConnections.remove(connection);
      }

      // Then rebind the connection to this pool. An exception is thrown
      // if the rebind fails since the connection does not support the
      // extranonce change.
      connection.rebindToPool(newPool);

      // And finally add the worker connection to the pool's worker
      // connections
      Set<WorkerConnection> newPoolConnections = getPoolWorkerConnections(newPool);
      newPoolConnections.add(connection);

      // Ask to the pool to authorize the worker
      // Create a fake authorization request since when a connection is
      // rebound, the miner does not send auhtorization request (since it
      // has already done it). But it may be the first time this
      // connection is bound to this pool, so the username on this
      // connection is not yet authorized on the pool.
      for (Entry<String, String> entry : connection.getAuthorizedWorkers().entrySet()) {
        MiningAuthorizeRequest fakeRequest = new MiningAuthorizeRequest();
        fakeRequest.setUsername(entry.getKey());
        fakeRequest.setPassword(entry.getValue());
        try {
          onAuthorizeRequest(connection, fakeRequest);
        } catch (AuthorizationException e) {
          LOGGER.error(
              "Authorization of user {} failed on pool {} when rebinding connection {}. Closing the connection. Cause: {}",
              entry.getKey(),
              newPool.getName(),
              connection.getConnectionName(),
              e.getMessage());
          connection.close();
        }
      }
    }
  }
  /**
   * Remove the pool with the given name.
   *
   * @param poolName
   * @throws NoPoolAvailableException
   */
  public void removePool(String poolName, Boolean keepHistory) throws NoPoolAvailableException {
    Pool pool = getPool(poolName);
    if (pool == null) {
      throw new NoPoolAvailableException("Pool with name " + poolName + " is not found");
    }

    pool.stopPool("Pool removed");
    pools.remove(pool);

    poolSwitchingStrategyManager.onPoolRemoved(pool);

    poolWorkerConnections.remove(pool);

    LOGGER.info("Pool {} removed.", poolName);

    // Remove the history if requested
    if (keepHistory != null && !keepHistory) {
      DatabaseManager.getInstance().deletePool(pool.getHost());
    }
  }
  /**
   * Set the priority of the pool with the given name and rebind worker connections based on this
   * new priority.
   *
   * @param poolName
   * @param newPriority
   * @throws BadParameterException
   */
  public void setPoolPriority(String poolName, int newPriority)
      throws NoPoolAvailableException, BadParameterException {
    if (getPool(poolName) == null) {
      throw new NoPoolAvailableException("Pool with name " + poolName + " not found");
    }

    if (newPriority < 0) {
      throw new BadParameterException("The priority has to be higher or equal to 0");
    }

    Pool pool = getPool(poolName);
    LOGGER.info(
        "Changing pool {} priority from {} to {}.",
        pool.getName(),
        pool.getPriority(),
        newPriority);
    pool.setPriority(newPriority);

    poolSwitchingStrategyManager.onPoolUpdated(pool);
  }
  /**
   * Called when a pool has sent a message to show.
   *
   * @param showMessage
   */
  public void onPoolShowMessage(Pool pool, ClientShowMessageNotification showMessage) {
    LOGGER.info(
        "\n*****************************\nMessage from pool {}: {}\n*****************************",
        pool.getName(),
        showMessage.getMessage());
    Set<WorkerConnection> connections = getPoolWorkerConnections(pool);

    if (connections != null && !connections.isEmpty()) {
      for (WorkerConnection connection : connections) {
        connection.onPoolShowMessage(showMessage);
      }
    }
  }
  /**
   * Called when a pool set the extranonce
   *
   * @param pool
   * @param setExtranonce
   */
  public void onPoolSetExtranonce(Pool pool, MiningSetExtranonceNotification setExtranonce) {
    LOGGER.info("Set the extranonce on pool {}.", pool.getName());

    Set<WorkerConnection> connections = getPoolWorkerConnections(pool);

    if (connections == null || connections.isEmpty()) {
      LOGGER.debug("No worker connections on pool {}. Do not send setExtranonce.", pool.getName());
    } else {
      for (WorkerConnection connection : connections) {
        try {
          connection.onPoolExtranonceChange();
        } catch (ChangeExtranonceNotSupportedException e) {
          connection.close();
          onWorkerDisconnection(
              connection,
              new Exception(
                  "The workerConnection "
                      + connection.getConnectionName()
                      + " does not support setExtranonce notification."));
        }
      }
    }
  }
  @Override
  public void onPoolUpdated(Pool poolUpdated) {
    List<Pool> pools = getProxyManager().getPools();
    checkPoolPriorities(pools);
    Collections.sort(
        pools,
        new Comparator<Pool>() {
          public int compare(Pool o1, Pool o2) {
            return o1.getPriority().compareTo(o2.getPriority());
          }
        });
    int newPriority = poolUpdated.getPriority();
    int previousPriority = newPriority;
    for (Pool pool : pools) {
      // Move the priority of other pools with lower or
      // equals priority
      if (pool.getPriority() == previousPriority && !pool.equals(poolUpdated)) {
        pool.setPriority(pool.getPriority() + 1);
        previousPriority = pool.getPriority();
      }
    }

    super.onPoolUpdated(poolUpdated);
  }
  /**
   * @param poolToUpdate
   * @throws URISyntaxException
   * @throws PoolStartException
   * @throws SocketException
   * @throws BadParameterException
   */
  public void updatePool(UpdatePoolDTO poolToUpdate)
      throws NotFoundException, SocketException, PoolStartException, URISyntaxException,
          BadParameterException {
    boolean hasBeenStopped = false;
    Pool pool = getPool(poolToUpdate.getName());

    if (pool == null) {
      throw new NotFoundException(
          "The pool with name " + poolToUpdate.getName() + " has not been found.");
    }

    checkUpdatePoolParameters(poolToUpdate);

    if (poolToUpdate.getHost() != null && !poolToUpdate.getHost().equals(pool.getHost())) {
      if (!hasBeenStopped) {
        pool.stopPool("Pool updated and needed to restart.");
      }
      hasBeenStopped = true;
      pool.setHost(poolToUpdate.getHost());
    }

    if (poolToUpdate.getIsExtranonceSubscribeEnabled() != null
        && !poolToUpdate
            .getIsExtranonceSubscribeEnabled()
            .equals(pool.getIsExtranonceSubscribeEnabled())) {
      if (!hasBeenStopped) {
        pool.stopPool("Pool updated and needed to restart.");
      }
      hasBeenStopped = true;
      pool.setIsExtranonceSubscribeEnabled(poolToUpdate.getIsExtranonceSubscribeEnabled());
    }

    if (poolToUpdate.getPassword() != null
        && !poolToUpdate.getPassword().equals(pool.getPassword())) {
      if (!hasBeenStopped) {
        pool.stopPool("Pool updated and needed to restart.");
      }
      hasBeenStopped = true;
      pool.setPassword(poolToUpdate.getPassword());
    }

    if (poolToUpdate.getUsername() != null
        && !poolToUpdate.getUsername().equals(pool.getUsername())) {
      if (!hasBeenStopped) {
        pool.stopPool("Pool updated and needed to restart.");
      }
      hasBeenStopped = true;
      pool.setUsername(poolToUpdate.getUsername());
    }

    if (poolToUpdate.getPriority() != null
        && !poolToUpdate.getPriority().equals(pool.getPriority())) {
      if (poolToUpdate.getPriority() < 0) {
        throw new BadParameterException("The priority has to be higher or equal to 0");
      }
      pool.setPriority(poolToUpdate.getPriority());
      if (poolSwitchingStrategyManager != null) {
        poolSwitchingStrategyManager.onPoolUpdated(pool);
      }
    }

    if (poolToUpdate.getWeight() != null && !poolToUpdate.getWeight().equals(pool.getWeight())) {
      if (poolToUpdate.getWeight() < 0) {
        throw new BadParameterException("The weight has to be higher or equal to 0");
      }
      pool.setWeight(poolToUpdate.getWeight());
      if (poolSwitchingStrategyManager != null) {
        poolSwitchingStrategyManager.onPoolUpdated(pool);
      }
    }

    if (poolToUpdate.getAppendWorkerNames() != null
        && !poolToUpdate.getAppendWorkerNames().equals(pool.isAppendWorkerNames())) {
      if (!hasBeenStopped) {
        pool.stopPool("Pool updated and needed to restart.");
      }
      hasBeenStopped = true;
      pool.setAppendWorkerNames(poolToUpdate.getAppendWorkerNames());
    }

    if (poolToUpdate.getWorkerNamesSeparator() != null
        && !poolToUpdate.getWorkerNamesSeparator().equals(pool.getWorkerSeparator())) {
      pool.setWorkerSeparator(poolToUpdate.getWorkerNamesSeparator());
    }

    if (poolToUpdate.getUseWorkerPassword() != null
        && !poolToUpdate.getUseWorkerPassword().equals(pool.isUseWorkerPassword())) {
      pool.setUseWorkerPassword(poolToUpdate.getUseWorkerPassword());
    }

    // If the pool has been stopped since some options needs a restart,
    // restart the pool.
    if (hasBeenStopped) {
      pool.startPool(this);
    }
  }
 /**
  * Called when a pool is now stable.
  *
  * @param pool
  */
 public void onPoolStable(Pool pool) {
   LOGGER.warn("Pool {} is STABLE.", pool.getName());
   poolSwitchingStrategyManager.onPoolStable(pool);
 }
  /**
   * Add the pool described in the given poolDTO
   *
   * @param addPoolDTO
   * @return
   * @throws URISyntaxException
   * @throws PoolStartException
   * @throws SocketException
   */
  public Pool addPool(AddPoolDTO addPoolDTO)
      throws BadParameterException, SocketException, PoolStartException, URISyntaxException {

    LOGGER.debug("Trying to add pool {}.", addPoolDTO);

    checkAddPoolParameters(addPoolDTO);

    Pool poolToAdd =
        new Pool(
            addPoolDTO.getPoolName(),
            addPoolDTO.getPoolHost(),
            addPoolDTO.getUsername(),
            addPoolDTO.getPassword());

    // By default, does not enable extranonce subscribe.
    poolToAdd.setExtranonceSubscribeEnabled(
        addPoolDTO.getEnableExtranonceSubscribe() != null
            && addPoolDTO.getEnableExtranonceSubscribe());

    poolToAdd.setAppendWorkerNames(
        addPoolDTO.getAppendWorkerNames() != null ? addPoolDTO.getAppendWorkerNames() : false);
    poolToAdd.setWorkerSeparator(
        addPoolDTO.getWorkerNameSeparator() != null
            ? addPoolDTO.getWorkerNameSeparator()
            : Constants.DEFAULT_WORKER_NAME_SEPARTOR);
    poolToAdd.setUseWorkerPassword(
        addPoolDTO.getUseWorkerPassword() != null ? addPoolDTO.getUseWorkerPassword() : false);

    if (addPoolDTO.getPriority() != null) {
      poolToAdd.setPriority(addPoolDTO.getPriority());
    }

    if (addPoolDTO.getWeight() != null) {
      poolToAdd.setWeight(addPoolDTO.getWeight());
    }

    // Add the pool to the pool list
    pools.add(poolToAdd);

    if (addPoolDTO.getPriority() != null) {
      try {
        setPoolPriority(addPoolDTO.getPoolName(), addPoolDTO.getPriority());
      } catch (NoPoolAvailableException e) {
        LOGGER.error("BUG DETECTED !!! This exceptin should not happen.", e);
      }
    }

    LOGGER.info("Pool added {}.", addPoolDTO);

    try {
      poolToAdd.setEnabled(addPoolDTO.getIsEnabled() == null || addPoolDTO.getIsEnabled());
    } catch (Exception e) {
      throw new PoolStartException(
          "Failed to enable the created pool with name "
              + poolToAdd.getName()
              + ". This should not happen. Surely a BUUUUGGGG !!!!",
          e);
    }

    if (poolToAdd.isEnabled()) {
      poolToAdd.startPool(this);
    }

    poolSwitchingStrategyManager.onPoolAdded(poolToAdd);

    return poolToAdd;
  }