@Override
  public List<String> getAllIndexedSpaceKeys() throws Exception {
    if (allIndexedSpacesKeys == null
        || allIndexedSpacesKeysNextRefresh < System.currentTimeMillis()) {
      allIndexedSpacesKeys = remoteSystemClient.getAllSpaces();
      if (spaceKeysExcluded != null) {
        allIndexedSpacesKeys.removeAll(spaceKeysExcluded);
      }
      allIndexedSpacesKeysNextRefresh = System.currentTimeMillis() + SPACES_REFRESH_TIME;
    }

    return allIndexedSpacesKeys;
  }
  /**
   * Configure the river.
   *
   * @param settings used for configuration.
   */
  @SuppressWarnings({"unchecked"})
  protected void configure(Map<String, Object> settings) {

    if (!closed) throw new IllegalStateException("Remote River must be stopped to configure it!");

    if (settings.containsKey("remote")) {
      Map<String, Object> remoteSettings = (Map<String, Object>) settings.get("remote");
      maxIndexingThreads =
          XContentMapValues.nodeIntegerValue(remoteSettings.get("maxIndexingThreads"), 1);

      SpaceIndexingMode sim =
          SpaceIndexingMode.parseConfiguration((String) remoteSettings.get("listDocumentsMode"));
      if (sim != null) spaceIndexingMode = sim;
      else if (XContentMapValues.nodeBooleanValue(remoteSettings.get("simpleGetDocuments"), false))
        spaceIndexingMode = SpaceIndexingMode.SIMPLE;

      indexFullUpdatePeriod =
          Utils.parseTimeValue(remoteSettings, "indexFullUpdatePeriod", 12, TimeUnit.HOURS);

      String ifuce = Utils.trimToNull((String) remoteSettings.get("indexFullUpdateCronExpression"));
      if (ifuce != null) {
        try {
          this.indexFullUpdateCronExpression = new CronExpression(ifuce);
        } catch (ParseException e) {
          throw new SettingsException(
              "Cron expression in indexFullUpdateCronExpression is invalid: " + e.getMessage());
        }
      }

      if (spaceIndexingMode.isIncrementalUpdateSupported()
          || (indexFullUpdatePeriod < 1 && indexFullUpdateCronExpression == null))
        indexUpdatePeriod =
            Utils.parseTimeValue(remoteSettings, "indexUpdatePeriod", 5, TimeUnit.MINUTES);
      else indexUpdatePeriod = 0;

      if (remoteSettings.containsKey("spacesIndexed")) {
        allIndexedSpacesKeys =
            Utils.parseCsvString(
                XContentMapValues.nodeStringValue(remoteSettings.get("spacesIndexed"), null));
        if (allIndexedSpacesKeys != null) {
          // stop spaces loading from remote system
          allIndexedSpacesKeysNextRefresh = Long.MAX_VALUE;
        }
      }
      if (remoteSettings.containsKey("spaceKeysExcluded")) {
        spaceKeysExcluded =
            Utils.parseCsvString(
                XContentMapValues.nodeStringValue(remoteSettings.get("spaceKeysExcluded"), null));
      }
      String remoteClientClass =
          Utils.trimToNull(
              XContentMapValues.nodeStringValue(remoteSettings.get("remoteClientClass"), null));
      if (remoteClientClass != null) {
        try {
          remoteSystemClient = (IRemoteSystemClient) Class.forName(remoteClientClass).newInstance();
        } catch (Exception e) {
          throw new SettingsException(
              "Unable to instantiate class defined by 'remote/remoteClientClass': "
                  + e.getMessage());
        }
      } else {
        remoteSystemClient = new GetJSONClient();
      }
      remoteSystemClient.init(
          this, remoteSettings, allIndexedSpacesKeysNextRefresh != Long.MAX_VALUE, this);
    } else {
      throw new SettingsException("'remote' element of river configuration structure not found");
    }

    Map<String, Object> indexSettings = null;
    if (settings.containsKey("index")) {
      indexSettings = (Map<String, Object>) settings.get("index");
      indexName = XContentMapValues.nodeStringValue(indexSettings.get("index"), riverName.name());
      typeName =
          XContentMapValues.nodeStringValue(
              indexSettings.get("type"), INDEX_DOCUMENT_TYPE_NAME_DEFAULT);
    } else {
      indexName = riverName.name();
      typeName = INDEX_DOCUMENT_TYPE_NAME_DEFAULT;
    }

    Map<String, Object> activityLogSettings = null;
    if (settings.containsKey("activity_log")) {
      activityLogSettings = (Map<String, Object>) settings.get("activity_log");
      activityLogIndexName =
          Utils.trimToNull(
              XContentMapValues.nodeStringValue(activityLogSettings.get("index"), null));
      if (activityLogIndexName == null) {
        throw new SettingsException(
            "'activity_log/index' element of river configuration structure must be defined with some string");
      }
      activityLogTypeName =
          Utils.trimToNull(
              XContentMapValues.nodeStringValue(
                  activityLogSettings.get("type"), INDEX_ACTIVITY_TYPE_NAME_DEFAULT));
    }

    documentIndexStructureBuilder =
        new DocumentWithCommentsIndexStructureBuilder(
            this, indexName, typeName, indexSettings, spaceIndexingMode.isUpdateDateMandatory());
    preparePreprocessors(indexSettings, documentIndexStructureBuilder);

    remoteSystemClient.setIndexStructureBuilder(documentIndexStructureBuilder);

    logger.info(
        "Configured Remote River '{}'. Search index name '{}', document type for issues '{}'. Indexing mode '{}'.",
        riverName.getName(),
        indexName,
        typeName,
        spaceIndexingMode.getConfigValue());
    if (activityLogIndexName != null) {
      logger.info(
          "Activity log for Remote River '{}' is enabled. Search index name '{}', document type for index updates '{}'.",
          riverName.getName(),
          activityLogIndexName,
          activityLogTypeName);
    }
  }