private void parseConfigFile()

in languagetool-server/src/main/java/org/languagetool/server/HTTPServerConfig.java [313:524]


  private void parseConfigFile(File file, boolean loadLangModel) {
    try {
      Properties props = new Properties();
      try (FileInputStream fis = new FileInputStream(file)) {
        props.load(fis);
        maxTextHardLength = Integer.parseInt(getOptionalProperty(props, "maxTextHardLength", Integer.toString(Integer.MAX_VALUE)));

        maxTextLengthAnonymous = maxTextLengthLoggedIn = maxTextLengthPremium = Integer.parseInt(getOptionalProperty(props, "maxTextLength", Integer.toString(Integer.MAX_VALUE)));
        maxTextLengthAnonymous = Integer.parseInt(getOptionalProperty(props, "maxTextLengthAnonymous", String.valueOf(maxTextLengthAnonymous)));
        maxTextLengthLoggedIn = Integer.parseInt(getOptionalProperty(props, "maxTextLengthLoggedIn", String.valueOf(maxTextLengthLoggedIn)));
        maxTextLengthPremium = Integer.parseInt(getOptionalProperty(props, "maxTextLengthPremium", String.valueOf(maxTextLengthPremium)));

        maxCheckTimeMillisAnonymous = maxCheckTimeMillisLoggedIn = maxCheckTimeMillisPremium = Integer.parseInt(getOptionalProperty(props, "maxCheckTimeMillis", "-1"));
        maxCheckTimeMillisAnonymous = Long.parseLong(getOptionalProperty(props, "maxCheckTimeMillisAnonymous", String.valueOf(maxCheckTimeMillisAnonymous)));
        maxCheckTimeMillisLoggedIn = Long.parseLong(getOptionalProperty(props, "maxCheckTimeMillisLoggedIn", String.valueOf(maxCheckTimeMillisLoggedIn)));
        maxCheckTimeMillisPremium = Long.parseLong(getOptionalProperty(props, "maxCheckTimeMillisPremium", String.valueOf(maxCheckTimeMillisPremium)));
        requestLimit = Integer.parseInt(getOptionalProperty(props, "requestLimit", "0"));
        requestLimitInBytes = Integer.parseInt(getOptionalProperty(props, "requestLimitInBytes", "0"));
        timeoutRequestLimit = Integer.parseInt(getOptionalProperty(props, "timeoutRequestLimit", "0"));
        requestLimitWhitelistUsers = Arrays.asList(getOptionalProperty(props, "requestLimitWhitelistUsers", "").split(",\\s*"));
        requestLimitWhitelistLimit = Integer.parseInt(getOptionalProperty(props, "requestLimitWhitelistLimit", "0"));
        pipelineCaching = Boolean.parseBoolean(getOptionalProperty(props, "pipelineCaching", "false").trim());
        pipelinePrewarming = Boolean.parseBoolean(getOptionalProperty(props, "pipelinePrewarming", "false").trim());
        maxPipelinePoolSize = Integer.parseInt(getOptionalProperty(props, "maxPipelinePoolSize", "5"));
        pipelineExpireTime = Integer.parseInt(getOptionalProperty(props, "pipelineExpireTimeInSeconds", "10"));
        requestLimitPeriodInSeconds = Integer.parseInt(getOptionalProperty(props, "requestLimitPeriodInSeconds", "0"));
        ipFingerprintFactor = Integer.parseInt(getOptionalProperty(props, "ipFingerprintFactor", "1"));
        trustXForwardForHeader = Boolean.valueOf(getOptionalProperty(props, "trustXForwardForHeader", "false").trim());
        maxWorkQueueSize = Integer.parseInt(getOptionalProperty(props, "maxWorkQueueSize", "0"));
        if (maxWorkQueueSize < 0) {
          throw new IllegalArgumentException("maxWorkQueueSize must be >= 0: " + maxWorkQueueSize);
        }
        minPort = Integer.parseInt(getOptionalProperty(props, "minPort", "0"));
        maxPort = Integer.parseInt(getOptionalProperty(props, "maxPort", "0"));
        String url = getOptionalProperty(props, "serverURL", null);
        setServerURL(url);
        String ruleIdToConfidence = getOptionalProperty(props, "ruleIdToConfidenceFile", null);
        if (ruleIdToConfidence != null) {
          ruleIdToConfidenceFile = new File(ruleIdToConfidence);
        }
        String langModel = getOptionalProperty(props, "languageModel", null);
        if (langModel != null && loadLangModel) {
          setLanguageModelDirectory(langModel);
        }
        String fasttextModel = getOptionalProperty(props, "fasttextModel", null);
        String fasttextBinary = getOptionalProperty(props, "fasttextBinary", null);
        if (fasttextBinary != null && fasttextModel != null) {
          setFasttextPaths(fasttextModel, fasttextBinary);
        }
        maxCheckThreads = Integer.parseInt(getOptionalProperty(props, "maxCheckThreads", "10"));
        if (maxCheckThreads < 1) {
          throw new IllegalArgumentException("Invalid value for maxCheckThreads, must be >= 1: " + maxCheckThreads);
        }
        // default value 0 = use maxCheckThreads setting (for compatibility)
        maxTextCheckerThreads = Integer.parseInt(getOptionalProperty(props, "maxTextCheckerThreads", "0"));
        if (maxTextCheckerThreads < 0) {
          throw new IllegalArgumentException("Invalid value for maxTextCheckerThreads, must be >= 1: " + maxTextCheckerThreads);
        }
        textCheckerQueueSize = Integer.parseInt(getOptionalProperty(props, "textCheckerQueueSize", "8"));
        if (textCheckerQueueSize < 0) {
          throw new IllegalArgumentException("Invalid value for textCheckerQueueSize, must be >= 1: " + textCheckerQueueSize);
        }

        boolean atdMode = getOptionalProperty(props, "mode", "LanguageTool").equalsIgnoreCase("AfterTheDeadline");
        if (atdMode) {
          throw new IllegalArgumentException("The AfterTheDeadline mode is not supported anymore in LanguageTool 3.8 or later");
        }
        String rulesConfigFilePath = getOptionalProperty(props, "rulesFile", null);
        if (rulesConfigFilePath != null) {
          rulesConfigFile = new File(rulesConfigFilePath);
          if (!rulesConfigFile.exists() || !rulesConfigFile.isFile()) {
            throw new RuntimeException("Rules Configuration file cannot be found: " + rulesConfigFile);
          }
        }
        String remoteRulesConfigFilePath = getOptionalProperty(props, "remoteRulesFile", null);
        if (remoteRulesConfigFilePath != null) {
          remoteRulesConfigFile = new File(remoteRulesConfigFilePath);
          if (!remoteRulesConfigFile.exists() || !remoteRulesConfigFile.isFile()) {
            throw new RuntimeException("Remote rules configuration file cannot be found: " + remoteRulesConfigFile);
          }
        }
        cacheSize = Integer.parseInt(getOptionalProperty(props, "cacheSize", "0"));
        if (cacheSize < 0) {
          throw new IllegalArgumentException("Invalid value for cacheSize: " + cacheSize + ", use 0 to deactivate cache");
        }
        if (props.containsKey("cacheTTLSeconds") && !props.containsKey("cacheSize")) {
          throw new IllegalArgumentException("Use of cacheTTLSeconds without also setting cacheSize has no effect.");
        }
        cacheTTLSeconds = Integer.parseInt(getOptionalProperty(props, "cacheTTLSeconds", "300"));
        maxErrorsPerWordRate = Float.parseFloat(getOptionalProperty(props, "maxErrorsPerWordRate", "0"));
        suggestionsEnabled = Boolean.parseBoolean(getOptionalProperty(props, "suggestionsEnabled", "true"));
        maxSpellingSuggestions = Integer.parseInt(getOptionalProperty(props, "maxSpellingSuggestions", "0"));
        blockedReferrers = Arrays.asList(getOptionalProperty(props, "blockedReferrers", "").split(",\\s*"));
        setTrustedSources(getOptionalProperty(props, "trustedSources", null));
        String premiumAlwaysValue = props.getProperty("premiumAlways");
        if (premiumAlwaysValue != null) {
          premiumAlways = Boolean.parseBoolean(premiumAlwaysValue.trim());
          if (premiumAlways) {
            System.out.println("*** Running in PREMIUM-ALWAYS mode");
          }
        }
        premiumOnly = Boolean.valueOf(getOptionalProperty(props, "premiumOnly", "false").trim());
        if (premiumOnly) {
          if (!Premium.isPremiumVersion()) {
            throw new IllegalArgumentException("Cannot use premiumOnly=true with non-premium version");
          }
          System.out.println("*** Running in PREMIUM-ONLY mode");
        }
        anonymousAccessAllowed = Boolean.valueOf(getOptionalProperty(props, "anonymousAccessAllowed", "true").trim());
        if (!anonymousAccessAllowed) {
          System.out.println("*** Running in RESTRICTED-ACCESS mode");
        }

        redisHost = getOptionalProperty(props, "redisHost", null);
        redisPort = Integer.parseInt(getOptionalProperty(props, "redisPort", "6379"));
        redisDatabase = Integer.parseInt(getOptionalProperty(props, "redisDatabase", "0"));
        redisUseSSL = Boolean.valueOf(getOptionalProperty(props, "redisUseSSL", "true").trim());
        redisPassword = getOptionalProperty(props, "redisPassword", null);
        redisDictTTL = Integer.parseInt(getOptionalProperty(props, "redisDictTTLSeconds", "600"));
        redisTimeout = Integer.parseInt(getOptionalProperty(props, "redisTimeoutMilliseconds", "100"));
        redisConnectionTimeout = Integer.parseInt(getOptionalProperty(props, "redisConnectionTimeoutMilliseconds", "5000"));

        redisCertificate = getOptionalProperty(props, "redisCertificate", null);
        redisKey = getOptionalProperty(props, "redisKey", null);
        redisKeyPassword = getOptionalProperty(props, "redisKeyPassword", null);

        redisUseSentinel = Boolean.parseBoolean(getOptionalProperty(props, "redisUseSentinel", "false").trim());
        sentinelHost = getOptionalProperty(props, "sentinelHost", null);
        sentinelPort = Integer.parseInt(getOptionalProperty(props, "sentinelPort", "26379"));
        sentinelPassword = getOptionalProperty(props, "sentinelPassword", null);
        sentinelMasterId = getOptionalProperty(props, "sentinelMasterId", null);

        gracefulDatabaseFailure = Boolean.parseBoolean(getOptionalProperty(props, "gracefulDatabaseFailure", "false").trim());
        dbDriver = getOptionalProperty(props, "dbDriver", null);
        dbUrl = getOptionalProperty(props, "dbUrl", null);
        dbUsername = getOptionalProperty(props, "dbUsername", null);
        dbPassword = getOptionalProperty(props, "dbPassword", null);
        dbTimeoutSeconds = Integer.parseInt(getOptionalProperty(props, "dbTimeoutSeconds", "10"));
        dbMaxConnections = Integer.parseInt(getOptionalProperty(props, "dbMaxConnections", "10"));
        databaseErrorRateThreshold = Integer.parseInt(getOptionalProperty(props, "dbErrorRateThreshold", "50"));
        databaseTimeoutRateThreshold = Integer.parseInt(getOptionalProperty(props, "dbTimeoutRateThreshold", "100"));
        databaseDownIntervalSeconds = Integer.parseInt(getOptionalProperty(props, "dbDownIntervalSeconds", "10"));
        dbLogging = Boolean.valueOf(getOptionalProperty(props, "dbLogging", "false").trim());
        passwortLoginAccessListPath = getOptionalProperty(props, "passwortLoginAccessListPath", "");
        prometheusMonitoring = Boolean.valueOf(getOptionalProperty(props, "prometheusMonitoring", "false").trim());
        prometheusPort = Integer.parseInt(getOptionalProperty(props, "prometheusPort", "9301"));
        skipLoggingRuleMatches = Boolean.valueOf(getOptionalProperty(props, "skipLoggingRuleMatches", "false").trim());
        skipLoggingChecks = Boolean.valueOf(getOptionalProperty(props, "skipLoggingChecks", "false").trim());
        if (dbLogging && (dbDriver == null || dbUrl == null || dbUsername == null || dbPassword == null)) {
          throw new IllegalArgumentException("dbLogging can only be true if dbDriver, dbUrl, dbUsername, and dbPassword are all set");
        }
        slowRuleLoggingThreshold = Integer.valueOf(getOptionalProperty(props, "slowRuleLoggingThreshold", "-1"));
        disabledRuleIds = Arrays.asList(getOptionalProperty(props, "disabledRuleIds", "").split(",\\s*"));
        localApiMode = Boolean.parseBoolean(getOptionalProperty(props, "localApiMode", "false"));
        motherTongue = getOptionalProperty(props, "motherTongue", "en-US");
        String preferredLanguages = getOptionalProperty(props, "preferredLanguages", "").replace(" ", "");
        if (!preferredLanguages.equals("")) {
          this.preferredLanguages = Arrays.asList(preferredLanguages.split(","));
        }
        dictLimitUser = Integer.valueOf(getOptionalProperty(props, "dictLimitUser", "0"));
        dictLimitTeam = Integer.valueOf(getOptionalProperty(props, "dictLimitTeam", "0"));
        styleGuideLimitUser = Integer.valueOf(getOptionalProperty(props, "styleGuideLimitUser", "0"));
        styleGuideLimitTeam = Integer.valueOf(getOptionalProperty(props, "styleGuideLimitTeam", "0"));
        requestLimitAccessToken = getOptionalProperty(props, "requestLimitAccessToken", null);
        jwtSecret = getOptionalProperty(props, "jwtSecret", null);
        externalRolloutServiceUrl = getOptionalProperty(props, "externalRolloutServiceUrl", null);
        externalRolloutServiceApiKey = getOptionalProperty(props, "externalRolloutServiceApiKey", null);

        globalConfig.setGrammalecteServer(getOptionalProperty(props, "grammalecteServer", null));
        globalConfig.setGrammalecteUser(getOptionalProperty(props, "grammalecteUser", null));
        globalConfig.setGrammalectePassword(getOptionalProperty(props, "grammalectePassword", null));
        String beolingusFile = getOptionalProperty(props, "beolingusFile", null);
        if (beolingusFile != null) {
          if (new File(beolingusFile).exists()) {
            globalConfig.setBeolingusFile(new File(beolingusFile));
          } else {
            throw new IllegalArgumentException("beolingusFile not found: " + beolingusFile);
          }
        }
        String nerUrl = getOptionalProperty(props, "nerUrl", null);
        if (nerUrl != null) {
          globalConfig.setNERUrl(nerUrl);
          logger.info("Using NER service: " + globalConfig.getNerUrl());
        }
        for (Object o : props.keySet()) {
          String key = (String)o;
          if (!KNOWN_OPTION_KEYS.contains(key) && !key.matches("lang-[a-z]+-dictPath") && !key.matches("lang-[a-z]+")) {
            System.err.println("***** WARNING: ****");
            System.err.println("Key '" + key + "' from configuration file '" + file + "' is unknown. Please check the key's spelling (case is significant).");
            System.err.println("Known keys: " + KNOWN_OPTION_KEYS);
          }
        }

        addDynamicLanguages(props);
        setAbTest(getOptionalProperty(props, "abTest", null));
        setAbTestClients(getOptionalProperty(props, "abTestClients", null));
        setAbTestRollout(Integer.parseInt(getOptionalProperty(props, "abTestRollout", "100")));
        String ngramLangIdentData = getOptionalProperty(props, "ngramLangIdentData", null);
        setDefaultThirdPartyAI(Boolean.parseBoolean(getOptionalProperty(props, "defaultThirdPartyAI", "false")));

        if (ngramLangIdentData != null) {
          File dir = new File(ngramLangIdentData);
          if (!dir.exists() || dir.isDirectory()) {
            throw new IllegalArgumentException("ngramLangIdentData does not exist or is a directory (needs to be a ZIP file): " + ngramLangIdentData);
          }
          setNgramLangIdentData(dir);
        }
      }
    } catch (IOException e) {
      throw new RuntimeException("Could not load properties from '" + file + "'", e);
    }
  }