001/*
002 * Copyright 2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2024 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2024 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.util;
037
038
039
040import java.util.ArrayList;
041import java.util.Collections;
042import java.util.List;
043import java.util.Properties;
044
045import static com.unboundid.util.UtilityMessages.*;
046
047
048
049/**
050 * This class provides a mechanism for retrieving the values of specified
051 * properties in the form of either Java system properties or process
052 * environment variables (using an alternative name generated from the provided
053 * property name using the
054 * {@link #generateEnvironmentVariableNameFromPropertyName} method).  System
055 * properties will be given a higher priority than environment variables, and
056 * the value can be parsed in accordance with a number of syntaxes.
057 */
058@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
059public final class PropertyManager
060{
061  /**
062   * Prevents this utility class from being instantiated.
063   */
064  private PropertyManager()
065  {
066    // No implementation is required.
067  }
068
069
070
071  /**
072   * Retrieves the value of the specified system property or environment
073   * variable.
074   *
075   * @param  propertyName  The name of the system property to retrieve, and to
076   *                       use to generate an alternative environment variable.
077   *                       It must not be {@code null} or empty.
078   *
079   * @return  The requested value, or {@code null} if it has not been set as
080   *          either a system property or an environment variable.
081   */
082  @Nullable()
083  public static String get(@NotNull final String propertyName)
084  {
085    return get(propertyName, null);
086  }
087
088
089
090  /**
091   * Retrieves the value of the specified system property or environment
092   * variable.
093   *
094   * @param  propertyName  The name of the system property to retrieve, and to
095   *                       use to generate an alternative environment variable.
096   *                       It must not be {@code null} or empty.
097   * @param  defaultValue  The default value to return if neither the system
098   *                       property nor associated environment variable have
099   *                       been set.  It may be {@code null} if no default value
100   *                       should be returned.
101   *
102   * @return  The requested value, or {@code null} if it has not been set as
103   *          either a system property or an environment variable.
104   */
105  @Nullable()
106  public static String get(@NotNull final String propertyName,
107                           @Nullable final String defaultValue)
108  {
109    final String systemPropertyValue =
110         StaticUtils.getSystemProperty(propertyName);
111    if (systemPropertyValue != null)
112    {
113      return systemPropertyValue;
114    }
115
116    final String environmentVariableValue =
117         StaticUtils.getEnvironmentVariable(propertyName);
118    if (environmentVariableValue != null)
119    {
120      return environmentVariableValue;
121    }
122
123    final String alternativeEnvironmentVariableName =
124         generateEnvironmentVariableNameFromPropertyName(propertyName);
125    if (! alternativeEnvironmentVariableName.equals(propertyName))
126    {
127      final String alternativeEnvironmentVariableValue =
128           StaticUtils.getEnvironmentVariable(
129                alternativeEnvironmentVariableName);
130      if (alternativeEnvironmentVariableValue != null)
131      {
132        return alternativeEnvironmentVariableValue;
133      }
134    }
135
136    return defaultValue;
137  }
138
139
140
141  /**
142   * Retrieves the value of the specified property or environment variable as a
143   * Boolean value.
144   *
145   * @param  propertyName  The name of the system property to retrieve, and to
146   *                       use to generate an alternative environment variable.
147   *                       It must not be {@code null} or empty.
148   *
149   * @return  The Boolean value of the specified property or environment
150   *          variable, or {@code null} if neither are set or are set to a
151   *          value that cannot be parsed as a Boolean.
152   */
153  @Nullable()
154  public static Boolean getBoolean(@NotNull final String propertyName)
155  {
156    return getBoolean(propertyName, null);
157  }
158
159
160
161  /**
162   * Retrieves the value of the specified property or environment variable as a
163   * Boolean value.
164   *
165   * @param  propertyName  The name of the system property to retrieve, and to
166   *                       use to generate an alternative environment variable.
167   *                       It must not be {@code null} or empty.
168   * @param  defaultValue  The default value to return if neither the system
169   *                       property nor associated environment variable have
170   *                       been set, or if the value cannot be parsed as a
171   *                       Boolean.  It may be {@code null} if no default value
172   *                       should be returned.
173   *
174   * @return  The Boolean value of the specified property or environment
175   *          variable, or the provided default value if neither are set or are
176   *          set to a value that cannot be parsed as a Boolean.
177   */
178  @Nullable()
179  public static Boolean getBoolean(@NotNull final String propertyName,
180                                   @Nullable final Boolean defaultValue)
181  {
182    return getBoolean(propertyName, defaultValue, false);
183  }
184
185
186
187  /**
188   * Retrieves the value of the specified property or environment variable as a
189   * Boolean value.
190   *
191   * @param  propertyName         The name of the system property to retrieve,
192   *                              and to use to generate an alternative
193   *                              environment variable.  It must not be
194   *                              {@code null} or empty.
195   * @param  defaultValue         The default value to return if neither the
196   *                              system property nor associated environment
197   *                              variable have been set, or if the value cannot
198   *                              be parsed as a Boolean and
199   *                              {@code throwOnInvalidValue} is {@code false}.
200   *                              It may be {@code null} if no default value
201   *                              should be returned.
202   * @param  throwOnInvalidValue  Indicates whether this method should throw an
203   *                              {@code IllegalArgumentException} if the
204   *                              system property or environment variable is
205   *                              set but its value cannot be parsed as a
206   *                              Boolean.
207   *
208   * @return  The Boolean value of the specified property or environment
209   *          variable, or the provided default value if neither are set or are
210   *          set to a value that cannot be parsed as a Boolean and
211   *          {@code throwOnInvalidValue} is {@code false}.
212   *
213   * @throws  IllegalArgumentException  If the property or environment variable
214   *                                    is set, but its value cannot be parsed
215   *                                    as a Boolean, and
216   *                                    {@code throwOnInvalidValue} is
217   *                                    {@code true}.
218   */
219  @Nullable()
220  public static Boolean getBoolean(@NotNull final String propertyName,
221                                   @Nullable final Boolean defaultValue,
222                                   final boolean throwOnInvalidValue)
223         throws IllegalArgumentException
224  {
225    final String stringValue = get(propertyName);
226    if (stringValue == null)
227    {
228      return defaultValue;
229    }
230
231    final String lowerValue = StaticUtils.toLowerCase(stringValue.trim());
232    switch (lowerValue)
233    {
234      case "true":
235      case "t":
236      case "yes":
237      case "y":
238      case "on":
239      case "1":
240        return Boolean.TRUE;
241      case "false":
242      case "f":
243      case "no":
244      case "n":
245      case "off":
246      case "0":
247        return Boolean.FALSE;
248      default:
249        if (throwOnInvalidValue)
250        {
251          throw new IllegalArgumentException(
252               ERR_PROPERTY_MANAGER_NOT_BOOLEAN.get(
253                    getIdentifierString(propertyName), stringValue));
254        }
255        else
256        {
257          return defaultValue;
258        }
259    }
260  }
261
262
263
264  /**
265   * Retrieves the value of the specified property or environment variable as an
266   * integer.
267   *
268   * @param  propertyName  The name of the system property to retrieve, and to
269   *                       use to generate an alternative environment variable.
270   *                       It must not be {@code null} or empty.
271   *
272   * @return  The integer value of the specified property or environment
273   *          variable, or {@code null} if neither are set or are set to a
274   *          value that cannot be parsed as an integer.
275   */
276  @Nullable()
277  public static Integer getInt(@NotNull final String propertyName)
278  {
279    return getInt(propertyName, null);
280  }
281
282
283
284  /**
285   * Retrieves the value of the specified property or environment variable as an
286   * integer.
287   *
288   * @param  propertyName  The name of the system property to retrieve, and to
289   *                       use to generate an alternative environment variable.
290   *                       It must not be {@code null} or empty.
291   * @param  defaultValue  The default value to return if neither the system
292   *                       property nor associated environment variable have
293   *                       been set, or if the value cannot be parsed as an
294   *                       integer.  It may be {@code null} if no default value
295   *                       should be returned.
296   *
297   * @return  The integer value of the specified property or environment
298   *          variable, or the provided default value if neither are set or are
299   *          set to a value that cannot be parsed as an integer.
300   */
301  @Nullable()
302  public static Integer getInt(@NotNull final String propertyName,
303                               @Nullable final Integer defaultValue)
304  {
305    return getInt(propertyName, defaultValue, false);
306  }
307
308
309
310  /**
311   * Retrieves the value of the specified property or environment variable as an
312   * integer.
313   *
314   * @param  propertyName         The name of the system property to retrieve,
315   *                              and to use to generate an alternative
316   *                              environment variable.  It must not be
317   *                              {@code null} or empty.
318   * @param  defaultValue         The default value to return if neither the
319   *                              system property nor associated environment
320   *                              variable have been set, or if the value cannot
321   *                              be parsed as an integer and
322   *                              {@code throwOnInvalidValue} is {@code false}.
323   *                              It may be {@code null} if no default value
324   *                              should be returned.
325   * @param  throwOnInvalidValue  Indicates whether this method should throw an
326   *                              {@code IllegalArgumentException} if the
327   *                              system property or environment variable is
328   *                              set but its value cannot be parsed as an
329   *                              integer.
330   *
331   * @return  The integer value of the specified property or environment
332   *          variable, or the provided default value if neither are set or are
333   *          set to a value that cannot be parsed as an integer and
334   *          {@code throwOnInvalidValue} is {@code false}.
335   *
336   * @throws  IllegalArgumentException  If the property or environment variable
337   *                                    is set, but its value cannot be parsed
338   *                                    as an integer, and
339   *                                    {@code throwOnInvalidValue} is
340   *                                    {@code true}.
341   */
342  @Nullable()
343  public static Integer getInt(@NotNull final String propertyName,
344                               @Nullable final Integer defaultValue,
345                               final boolean throwOnInvalidValue)
346         throws IllegalArgumentException
347  {
348    final String stringValue = get(propertyName);
349    if (stringValue == null)
350    {
351      return defaultValue;
352    }
353
354    try
355    {
356      return Integer.parseInt(stringValue.trim());
357    }
358    catch (final Exception e)
359    {
360      Debug.debugException(e);
361      if (throwOnInvalidValue)
362      {
363        throw new IllegalArgumentException(
364             ERR_PROPERTY_MANAGER_NOT_INT.get(getIdentifierString(propertyName),
365                  stringValue),
366             e);
367      }
368      else
369      {
370        return defaultValue;
371      }
372    }
373  }
374
375
376
377  /**
378   * Retrieves the value of the specified property or environment variable as a
379   * long.
380   *
381   * @param  propertyName  The name of the system property to retrieve, and to
382   *                       use to generate an alternative environment variable.
383   *                       It must not be {@code null} or empty.
384   *
385   * @return  The long value of the specified property or environment variable,
386   *          or {@code null} if neither are set or are set to a value that
387   *          cannot be parsed as a long.
388   */
389  @Nullable()
390  public static Long getLong(@NotNull final String propertyName)
391  {
392    return getLong(propertyName, null);
393  }
394
395
396
397  /**
398   * Retrieves the value of the specified property or environment variable as a
399   * long.
400   *
401   * @param  propertyName  The name of the system property to retrieve, and to
402   *                       use to generate an alternative environment variable.
403   *                       It must not be {@code null} or empty.
404   * @param  defaultValue  The default value to return if neither the system
405   *                       property nor associated environment variable have
406   *                       been set, or if the value cannot be parsed as a long.
407   *                       It may be {@code null} if no default value should be
408   *                       returned.
409   *
410   * @return  The long value of the specified property or environment variable,
411   *          or the provided default value if neither are set or are set to a
412   *          value that cannot be parsed as a long.
413   */
414  @Nullable()
415  public static Long getLong(@NotNull final String propertyName,
416                             @Nullable final Long defaultValue)
417  {
418    return getLong(propertyName, defaultValue, false);
419  }
420
421
422
423  /**
424   * Retrieves the value of the specified property or environment variable as a
425   * long.
426   *
427   * @param  propertyName         The name of the system property to retrieve,
428   *                              and to use to generate an alternative
429   *                              environment variable.  It must not be
430   *                              {@code null} or empty.
431   * @param  defaultValue         The default value to return if neither the
432   *                              system property nor associated environment
433   *                              variable have been set, or if the value cannot
434   *                              be parsed as a long and
435   *                              {@code throwOnInvalidValue} is {@code false}.
436   *                              It may be {@code null} if no default value
437   *                              should be returned.
438   * @param  throwOnInvalidValue  Indicates whether this method should throw an
439   *                              {@code IllegalArgumentException} if the
440   *                              system property or environment variable is
441   *                              set but its value cannot be parsed as a
442   *                              long.
443   *
444   * @return  The long value of the specified property or environment variable,
445   *          or the provided default value if neither are set or are set to a
446   *          value that cannot be parsed as a long and
447   *          {@code throwOnInvalidValue} is {@code false}.
448   *
449   * @throws  IllegalArgumentException  If the property or environment variable
450   *                                    is set, but its value cannot be parsed
451   *                                    as a long, and
452   *                                    {@code throwOnInvalidValue} is
453   *                                    {@code true}.
454   */
455  @Nullable()
456  public static Long getLong(@NotNull final String propertyName,
457                             @Nullable final Long defaultValue,
458                             final boolean throwOnInvalidValue)
459         throws IllegalArgumentException
460  {
461    final String stringValue = get(propertyName);
462    if (stringValue == null)
463    {
464      return defaultValue;
465    }
466
467    try
468    {
469      return Long.parseLong(stringValue.trim());
470    }
471    catch (final Exception e)
472    {
473      Debug.debugException(e);
474      if (throwOnInvalidValue)
475      {
476        throw new IllegalArgumentException(
477             ERR_PROPERTY_MANAGER_NOT_LONG.get(
478                  getIdentifierString(propertyName), stringValue),
479             e);
480      }
481      else
482      {
483        return defaultValue;
484      }
485    }
486  }
487
488
489
490  /**
491   * Retrieves the value of the specified property or environment variable as
492   * a list of comma-delimited values.  Any spaces around commas will be
493   * trimmed.
494   *
495   * @param  propertyName  The name of the system property to retrieve, and to
496   *                       use to generate an alternative environment variable.
497   *                       It must not be {@code null} or empty.
498   *
499   * @return  An unmodifiable list containing the comma-delimited values of the
500   *          specified property or environment variable, or an empty list if
501   *          neither is set.
502   */
503  @NotNull()
504  public static List<String> getCommaDelimitedList(
505              @NotNull final String propertyName)
506  {
507    return getCommaDelimitedList(propertyName, true);
508  }
509
510
511
512  /**
513   * Retrieves the value of the specified property or environment variable as
514   * a list of comma-delimited values.  Any spaces around commas will be
515   * trimmed.
516   *
517   * @param  propertyName  The name of the system property to retrieve, and to
518   *                       use to generate an alternative environment variable.
519   *                       It must not be {@code null} or empty.
520   * @param  trimItems     Indicates whether the individual items in the list
521   *                       should be trimmed to remove leading and/or trailing
522   *                       spaces.
523   *
524   * @return  An unmodifiable list containing the comma-delimited values of the
525   *          specified property or environment variable, or an empty list if
526   *          neither is set.
527   */
528  @NotNull()
529  public static List<String> getCommaDelimitedList(
530              @NotNull final String propertyName,
531              final boolean trimItems)
532  {
533    final List<String> items = new ArrayList<>();
534
535    final String stringValue = get(propertyName);
536    if (stringValue != null)
537    {
538      int startPos = 0;
539      while (true)
540      {
541        final int commaPos = stringValue.indexOf(',', startPos);
542        if (commaPos < 0)
543        {
544          String substring = stringValue.substring(startPos);
545          if (trimItems)
546          {
547            substring = substring.trim();
548          }
549
550          items.add(substring);
551          break;
552        }
553        else
554        {
555          String substring = stringValue.substring(startPos, commaPos);
556          if (trimItems)
557          {
558            substring = substring.trim();
559          }
560
561          items.add(substring);
562          startPos = commaPos + 1;
563        }
564      }
565    }
566
567    return Collections.unmodifiableList(items);
568  }
569
570
571
572  /**
573   * Retrieves a {@code Properties} object with values for any of the specified
574   * properties that are currently set.
575   *
576   * @param  propertyNames  The name of the properties whose values should be
577   *                        retrieved.  It must not be {@code null}, but may be
578   *                        empty.
579   *
580   * @return  A {@code Properties} object with any of the specified properties
581   *          that are set as either JVM system properties or environment
582   *          variables.  It may be empty if none of the specified properties
583   *          have been set.
584   */
585  @NotNull()
586  public static Properties getProperties(@NotNull final String... propertyNames)
587  {
588    final Properties properties = new Properties();
589
590    for (final String propertyName : propertyNames)
591    {
592      final String propertyValue = get(propertyName);
593      if (propertyValue != null)
594      {
595        properties.setProperty(propertyName, propertyValue);
596      }
597    }
598
599    return properties;
600  }
601
602
603
604  /**
605   * Generates an alternative environment variable name that can be used for a
606   * given property name.  All alphabetic letters in the provided name will be
607   * converted to uppercase, and all characters other than ASCII letters and
608   * digits will be converted to underscores.
609   *
610   * @param  propertyName  The property name to use to generate the environment
611   *                       variable name.  It must not be {@code null} or empty.
612   *
613   * @return  The alternative environment variable name generated from the given
614   *          property name.
615   */
616  @NotNull()
617  public static String generateEnvironmentVariableNameFromPropertyName(
618              @NotNull final String propertyName)
619  {
620    final String upperPropertyName =
621         StaticUtils.toUpperCase(propertyName.trim());
622
623    final int length = upperPropertyName.length();
624    final StringBuilder buffer = new StringBuilder(length);
625    for (int i=0; i < length; i++)
626    {
627      final char c = upperPropertyName.charAt(i);
628      if (((c >= 'A') && (c <= 'Z')) ||
629           ((c >= '0') && (c <= '9')))
630      {
631        buffer.append(c);
632      }
633      else
634      {
635        buffer.append('_');
636      }
637    }
638
639    return buffer.toString();
640  }
641
642
643
644  /**
645   * Retrieves an identifier string that can be used to indicate how the value
646   * of the specified property was obtained.  The returned value will be one of:
647   * <UL>
648   *   <LI>system property '{propertyName}'</LI>
649   *   <LI>environment variable '{propertyName}'</LI>
650   *   <LI>environment variable '{alternativeName}'</LI>
651   * </UL>
652   *
653   * @param  propertyName  The property name for which to retrieve the
654   *                       identifier.
655   *
656   * @return  The identifier string for the provided property name, or
657   *          {@code null} if the specified property is not set as either a
658   *          system property or an environment variable (including with an
659   *          alternative name).
660   */
661  @Nullable()
662  static String getIdentifierString(@NotNull final String propertyName)
663  {
664    if (StaticUtils.getSystemProperty(propertyName) != null)
665    {
666      return INFO_PROPERTY_MANAGER_SYSTEM_PROPERY_IDENTIFIER.get(propertyName);
667    }
668
669    if (StaticUtils.getEnvironmentVariable(propertyName) != null)
670    {
671      return INFO_PROPERTY_MANAGER_ENVIRONMENT_VARIABLE_IDENTIFIER.get(
672           propertyName);
673    }
674
675    final String alternativeName =
676         generateEnvironmentVariableNameFromPropertyName(propertyName);
677    if (StaticUtils.getEnvironmentVariable(alternativeName) != null)
678    {
679      return INFO_PROPERTY_MANAGER_ENVIRONMENT_VARIABLE_IDENTIFIER.get(
680           alternativeName);
681    }
682
683    return null;
684  }
685}