DaemonConfiguration.java

  1. /*
  2.  *  Licensed to the Apache Software Foundation (ASF) under one or more
  3.  *  contributor license agreements.  See the NOTICE file distributed with
  4.  *  this work for additional information regarding copyright ownership.
  5.  *  The ASF licenses this file to You under the Apache License, Version 2.0
  6.  *  (the "License"); you may not use this file except in compliance with
  7.  *  the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  *  Unless required by applicable law or agreed to in writing, software
  12.  *  distributed under the License is distributed on an "AS IS" BASIS,
  13.  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  *  See the License for the specific language governing permissions and
  15.  *  limitations under the License.
  16.  */

  17. package org.apache.commons.daemon.support;

  18. import java.io.FileInputStream;
  19. import java.io.IOException;
  20. import java.io.InputStream;
  21. import java.util.ArrayList;
  22. import java.util.Properties;
  23. import java.text.ParseException;

  24. /**
  25.  * Used by jsvc for Daemon configuration.
  26.  * <p>
  27.  * Configuration is read from properties file.
  28.  * If no properties file is given the {@code daemon.properties}
  29.  * is used from the current directory.
  30.  * </p>
  31.  * <p>
  32.  * The properties file can have property values expanded at runtime
  33.  * by using System properties or execution environment. The part
  34.  * of the property value between {@code ${} and {@code }}
  35.  * will be used as System property or environment key. If found then
  36.  * the entire {@code ${foo}} will be replaced by the value of
  37.  * either system property or environment variable named {@code foo}.
  38.  * </p>
  39.  * <p>
  40.  * If no variable is found the {@code ${foo}}  will be passed as is.
  41.  * In case of {@code $${foo}} this will be unescaped and resulting
  42.  * value will be {@code ${foo}}.
  43.  * </p>
  44.  */
  45. public final class DaemonConfiguration
  46. {
  47.     /**
  48.      * Default configuration file name.
  49.      */
  50.     protected final static String DEFAULT_CONFIG        = "daemon.properties";
  51.     /**
  52.      * Property prefix
  53.      */
  54.     protected final static String PREFIX                = "daemon.";
  55.     private   final static String BTOKEN                = "${";
  56.     private   final static String ETOKEN                = "}";

  57.     private final Properties configurationProperties;
  58.     private final Properties systemProperties;

  59.     /**
  60.      * An empty immutable {@code String} array.
  61.      */
  62.     static final String[] EMPTY_STRING_ARRAY = {};

  63.     /**
  64.      * Default constructor
  65.      */
  66.     public DaemonConfiguration()
  67.     {
  68.         configurationProperties = new Properties();
  69.         systemProperties        = System.getProperties();
  70.     }

  71.     /**
  72.      * Loads the configuration properties file.
  73.      *
  74.      * @param fileName The properties file to load.
  75.      * @return {@code true} if the file was loaded.
  76.      */
  77.     public boolean load(String fileName)
  78.     {
  79.         if (fileName == null) {
  80.             fileName = DEFAULT_CONFIG;
  81.         }

  82.         try (InputStream inputStream = new FileInputStream(fileName)) {
  83.             configurationProperties.clear();
  84.             configurationProperties.load(inputStream);
  85.             return true;
  86.         } catch (final IOException ex) {
  87.             // Error reading properties file
  88.             return false;
  89.         }
  90.     }

  91.     private String expandProperty(final String propValue)
  92.         throws ParseException
  93.     {
  94.         final StringBuilder expanded;
  95.         int btoken;
  96.         int ctoken = 0;

  97.         if (propValue == null) {
  98.             return null;
  99.         }
  100.         expanded = new StringBuilder();
  101.         btoken   = propValue.indexOf(BTOKEN);
  102.         while (btoken != -1) {
  103.             if (btoken > 0 && propValue.charAt(btoken - 1) == BTOKEN.charAt(0)) {
  104.                 // Skip and unquote.
  105.                 expanded.append(propValue.substring(ctoken, btoken));
  106.                 ctoken = btoken + 1;
  107.                 btoken = propValue.indexOf(BTOKEN, btoken + BTOKEN.length());
  108.                 continue;
  109.             }
  110.             final int etoken = propValue.indexOf(ETOKEN, btoken);
  111.             if (etoken == -1) {
  112.                 // We have "${" without "}"
  113.                 throw new ParseException("Error while looking for teminating '" +
  114.                                          ETOKEN + "'", btoken);
  115.             }
  116.             final String variable = propValue.substring(btoken + BTOKEN.length(), etoken);
  117.             String sysvalue = systemProperties.getProperty(variable);
  118.             if (sysvalue == null) {
  119.                 // Try with the environment if there was no
  120.                 // property by that name.
  121.                 sysvalue = System.getenv(variable);
  122.             }
  123.             if (sysvalue != null) {
  124.                 final String strtoken = propValue.substring(ctoken, btoken);
  125.                 expanded.append(strtoken);
  126.                 expanded.append(sysvalue);
  127.                 ctoken = etoken + ETOKEN.length();
  128.             }
  129.             btoken = propValue.indexOf(BTOKEN, etoken + ETOKEN.length());
  130.         }
  131.         // Add what's left.
  132.         expanded.append(propValue.substring(ctoken));
  133.         return expanded.toString();
  134.     }

  135.     /**
  136.      * Gets the configuration property.
  137.      *
  138.      * @param name The name of the property to get.
  139.      * @throws ParseException if the property is wrongly formatted.
  140.      * @return  Configuration property including any expansion/replacement
  141.      */
  142.     public String getProperty(final String name)
  143.         throws ParseException
  144.     {
  145.         if (name == null) {
  146.             return null;
  147.         }
  148.         return expandProperty(configurationProperties.getProperty(PREFIX + name));
  149.     }

  150.     /**
  151.      * Gets the configuration property array.
  152.      * <p>
  153.      * Property array is constructed form the list of properties
  154.      * which end with {@code [index]}
  155.      * </p>
  156.      * <pre>
  157.      * daemon.arg[0] = argument 1
  158.      * daemon.arg[1] = argument 2
  159.      * daemon.arg[2] = argument 3
  160.      * </pre>
  161.      * @param name The name of the property array to get.
  162.      * @throws ParseException if the property is wrongly formatted.
  163.      * @return  Configuration property array including any expansion/replacement
  164.      */
  165.     public String[] getPropertyArray(final String name)
  166.         throws ParseException
  167.     {
  168.         final ArrayList<String> list = new ArrayList<>();
  169.         String    args;

  170.         // Load daemon.arg[0] ... daemon.arg[n] into the String array.
  171.         //
  172.         while ((args = getProperty(name + "[" + list.size() + "]")) != null) {
  173.             list.add(args);
  174.         }
  175.         return list.toArray(EMPTY_STRING_ARRAY);
  176.     }
  177. }