/**
   * Creates new HTTP requests handler.
   *
   * @param hnd Handler.
   * @param authChecker Authentication checking closure.
   * @param log Logger.
   */
  GridJettyRestHandler(
      GridRestProtocolHandler hnd, GridClosure<String, Boolean> authChecker, GridLogger log) {
    assert hnd != null;
    assert log != null;

    this.hnd = hnd;
    this.log = log;
    this.authChecker = authChecker;

    // Init default page and favicon.
    try {
      initDefaultPage();

      if (log.isDebugEnabled()) log.debug("Initialized default page.");
    } catch (IOException e) {
      U.warn(log, "Failed to initialize default page: " + e.getMessage());
    }

    try {
      initFavicon();

      if (log.isDebugEnabled())
        log.debug(
            favicon != null ? "Initialized favicon, size: " + favicon.length : "Favicon is null.");
    } catch (IOException e) {
      U.warn(log, "Failed to initialize favicon: " + e.getMessage());
    }
  }
  /** {@inheritDoc} */
  @SuppressWarnings("deprecation")
  @Override
  public void start() throws IgniteException {
    if (sesFactory == null && F.isEmpty(hibernateCfgPath))
      throw new IgniteException(
          "Either session factory or Hibernate configuration file is required by "
              + getClass().getSimpleName()
              + '.');

    if (!F.isEmpty(hibernateCfgPath)) {
      if (sesFactory == null) {
        try {
          URL url = new URL(hibernateCfgPath);

          sesFactory = new Configuration().configure(url).buildSessionFactory();
        } catch (MalformedURLException ignored) {
          // No-op.
        }

        if (sesFactory == null) {
          File cfgFile = new File(hibernateCfgPath);

          if (cfgFile.exists())
            sesFactory = new Configuration().configure(cfgFile).buildSessionFactory();
        }

        if (sesFactory == null)
          sesFactory = new Configuration().configure(hibernateCfgPath).buildSessionFactory();

        if (sesFactory == null)
          throw new IgniteException(
              "Failed to resolve Hibernate configuration file: " + hibernateCfgPath);

        closeSesOnStop = true;
      } else
        U.warn(
            log,
            "Hibernate configuration file configured in "
                + getClass().getSimpleName()
                + " will be ignored (session factory is already set).");
    }
  }
  /** {@inheritDoc} */
  @Override
  public void close() {
    closed = true;

    U.closeQuiet(srvSock);

    if (gcWorker != null) {
      U.cancel(gcWorker);

      // This method may be called from already interrupted thread.
      // Need to ensure cleaning on close.
      boolean interrupted = Thread.interrupted();

      try {
        U.join(gcWorker);
      } catch (IgniteInterruptedCheckedException e) {
        U.warn(log, "Interrupted when stopping GC worker.", e);
      } finally {
        if (interrupted) Thread.currentThread().interrupt();
      }
    }
  }
  /**
   * Initializes store.
   *
   * @throws GridException If failed to initialize.
   */
  private void init() throws GridException {
    if (initGuard.compareAndSet(false, true)) {
      if (log.isDebugEnabled()) log.debug("Initializing cache store.");

      try {
        if (sesFactory != null)
          // Session factory has been provided - nothing to do.
          return;

        if (!F.isEmpty(hibernateCfgPath)) {
          try {
            URL url = new URL(hibernateCfgPath);

            sesFactory = new Configuration().configure(url).buildSessionFactory();

            if (log.isDebugEnabled()) log.debug("Configured session factory using URL: " + url);

            // Session factory has been successfully initialized.
            return;
          } catch (MalformedURLException e) {
            if (log.isDebugEnabled())
              log.debug("Caught malformed URL exception: " + e.getMessage());
          }

          // Provided path is not a valid URL. File?
          File cfgFile = new File(hibernateCfgPath);

          if (cfgFile.exists()) {
            sesFactory = new Configuration().configure(cfgFile).buildSessionFactory();

            if (log.isDebugEnabled())
              log.debug("Configured session factory using file: " + hibernateCfgPath);

            // Session factory has been successfully initialized.
            return;
          }

          // Provided path is not a file. Classpath resource?
          sesFactory = new Configuration().configure(hibernateCfgPath).buildSessionFactory();

          if (log.isDebugEnabled())
            log.debug("Configured session factory using classpath resource: " + hibernateCfgPath);
        } else {
          if (hibernateProps == null) {
            U.warn(
                log, "No Hibernate configuration has been provided for store (will use default).");

            hibernateProps = new Properties();

            hibernateProps.setProperty("hibernate.connection.url", DFLT_CONN_URL);
            hibernateProps.setProperty("hibernate.show_sql", DFLT_SHOW_SQL);
            hibernateProps.setProperty("hibernate.hbm2ddl.auto", DFLT_HBM2DDL_AUTO);
          }

          Configuration cfg = new Configuration();

          cfg.setProperties(hibernateProps);

          assert resourceAvailable(MAPPING_RESOURCE);

          cfg.addResource(MAPPING_RESOURCE);

          sesFactory = cfg.buildSessionFactory();

          if (log.isDebugEnabled())
            log.debug("Configured session factory using properties: " + hibernateProps);
        }
      } catch (HibernateException e) {
        throw new GridException("Failed to initialize store.", e);
      } finally {
        initLatch.countDown();
      }
    } else if (initLatch.getCount() > 0) U.await(initLatch);

    if (sesFactory == null) throw new GridException("Cache store was not properly initialized.");
  }
  /** {@inheritDoc} */
  @SuppressWarnings("BusyWait")
  @Override
  public void start(GridRestProtocolHandler hnd) throws GridException {
    InetAddress locHost;

    try {
      locHost = U.resolveLocalHost(ctx.config().getLocalHost());
    } catch (IOException e) {
      throw new GridException(
          "Failed to resolve local host to bind address: " + ctx.config().getLocalHost(), e);
    }

    System.setProperty(GG_JETTY_HOST, locHost.getHostAddress());

    jettyHnd =
        new GridJettyRestHandler(
            hnd,
            new C1<String, Boolean>() {
              @Override
              public Boolean apply(String tok) {
                return F.isEmpty(secretKey) || authenticate(tok);
              }
            },
            log);

    String jettyPath = ctx.config().getRestJettyPath();

    final URL cfgUrl;

    if (jettyPath == null) {
      cfgUrl = null;

      if (log.isDebugEnabled())
        log.debug("Jetty configuration file is not provided, using defaults.");
    } else {
      cfgUrl = U.resolveGridGainUrl(jettyPath);

      if (cfgUrl == null)
        throw new GridSpiException("Invalid Jetty configuration file: " + jettyPath);
      else if (log.isDebugEnabled()) log.debug("Jetty configuration file: " + cfgUrl);
    }

    loadJettyConfiguration(cfgUrl);

    AbstractNetworkConnector connector = getJettyConnector();

    try {
      host = InetAddress.getByName(connector.getHost());
    } catch (UnknownHostException e) {
      throw new GridException("Failed to resolve Jetty host address: " + connector.getHost(), e);
    }

    int initPort = connector.getPort();

    int lastPort = initPort + ctx.config().getRestPortRange() - 1;

    for (port = initPort; port <= lastPort; port++) {
      connector.setPort(port);

      if (startJetty()) {
        if (log.isInfoEnabled()) log.info(startInfo());

        return;
      }
    }

    U.warn(
        log,
        "Failed to start Jetty REST server (possibly all ports in range are in use) "
            + "[firstPort="
            + initPort
            + ", lastPort="
            + lastPort
            + ']');
  }
  /** {@inheritDoc} */
  @SuppressWarnings("BusyWait")
  @Override
  public void start(final GridRestProtocolHandler hnd) throws GridException {
    assert hnd != null;

    GridConfiguration cfg = ctx.config();

    GridNioServerListener<GridClientMessage> lsnr = new GridTcpRestNioListener(log, hnd);

    GridNioParser parser = new GridTcpRestParser(log);

    try {
      host = resolveRestTcpHost(cfg);

      SSLContext sslCtx = null;

      if (cfg.isRestTcpSslEnabled()) {
        GridSslContextFactory factory = cfg.getRestTcpSslContextFactory();

        if (factory == null)
          // Thrown SSL exception instead of GridException for writing correct warning message into
          // log.
          throw new SSLException("SSL is enabled, but SSL context factory is not specified.");

        sslCtx = factory.createSslContext();
      }

      int lastPort = cfg.getRestTcpPort() + cfg.getRestPortRange() - 1;

      for (port = cfg.getRestTcpPort(); port <= lastPort; port++) {
        if (startTcpServer(host, port, lsnr, parser, sslCtx, cfg)) {
          if (log.isInfoEnabled()) log.info(startInfo());

          return;
        }
      }

      U.warn(
          log,
          "Failed to start TCP binary REST server (possibly all ports in range are in use) "
              + "[firstPort="
              + cfg.getRestTcpPort()
              + ", lastPort="
              + lastPort
              + ", host="
              + host
              + ']');
    } catch (SSLException e) {
      U.warn(
          log,
          "Failed to start " + name() + " protocol on port " + port + ": " + e.getMessage(),
          "Failed to start "
              + name()
              + " protocol on port "
              + port
              + ". Check if SSL context factory is "
              + "properly configured.");
    } catch (IOException e) {
      U.warn(
          log,
          "Failed to start " + name() + " protocol on port " + port + ": " + e.getMessage(),
          "Failed to start "
              + name()
              + " protocol on port "
              + port
              + ". "
              + "Check restTcpHost configuration property.");
    }
  }