DaemonConfiguration.java
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.daemon.support;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Properties;
import java.text.ParseException;
/**
* Used by jsvc for Daemon configuration.
* <p>
* Configuration is read from properties file.
* If no properties file is given the {@code daemon.properties}
* is used from the current directory.
* </p>
* <p>
* The properties file can have property values expanded at runtime
* by using System properties or execution environment. The part
* of the property value between {@code ${} and {@code }}
* will be used as System property or environment key. If found then
* the entire {@code ${foo}} will be replaced by the value of
* either system property or environment variable named {@code foo}.
* </p>
* <p>
* If no variable is found the {@code ${foo}} will be passed as is.
* In case of {@code $${foo}} this will be unescaped and resulting
* value will be {@code ${foo}}.
* </p>
*/
public final class DaemonConfiguration
{
/**
* Default configuration file name.
*/
protected final static String DEFAULT_CONFIG = "daemon.properties";
/**
* Property prefix
*/
protected final static String PREFIX = "daemon.";
private final static String BTOKEN = "${";
private final static String ETOKEN = "}";
private final Properties configurationProperties;
private final Properties systemProperties;
/**
* An empty immutable {@code String} array.
*/
static final String[] EMPTY_STRING_ARRAY = {};
/**
* Default constructor
*/
public DaemonConfiguration()
{
configurationProperties = new Properties();
systemProperties = System.getProperties();
}
/**
* Loads the configuration properties file.
*
* @param fileName The properties file to load.
* @return {@code true} if the file was loaded.
*/
public boolean load(String fileName)
{
if (fileName == null) {
fileName = DEFAULT_CONFIG;
}
try (InputStream inputStream = new FileInputStream(fileName)) {
configurationProperties.clear();
configurationProperties.load(inputStream);
return true;
} catch (final IOException ex) {
// Error reading properties file
return false;
}
}
private String expandProperty(final String propValue)
throws ParseException
{
final StringBuilder expanded;
int btoken;
int ctoken = 0;
if (propValue == null) {
return null;
}
expanded = new StringBuilder();
btoken = propValue.indexOf(BTOKEN);
while (btoken != -1) {
if (btoken > 0 && propValue.charAt(btoken - 1) == BTOKEN.charAt(0)) {
// Skip and unquote.
expanded.append(propValue.substring(ctoken, btoken));
ctoken = btoken + 1;
btoken = propValue.indexOf(BTOKEN, btoken + BTOKEN.length());
continue;
}
final int etoken = propValue.indexOf(ETOKEN, btoken);
if (etoken == -1) {
// We have "${" without "}"
throw new ParseException("Error while looking for teminating '" +
ETOKEN + "'", btoken);
}
final String variable = propValue.substring(btoken + BTOKEN.length(), etoken);
String sysvalue = systemProperties.getProperty(variable);
if (sysvalue == null) {
// Try with the environment if there was no
// property by that name.
sysvalue = System.getenv(variable);
}
if (sysvalue != null) {
final String strtoken = propValue.substring(ctoken, btoken);
expanded.append(strtoken);
expanded.append(sysvalue);
ctoken = etoken + ETOKEN.length();
}
btoken = propValue.indexOf(BTOKEN, etoken + ETOKEN.length());
}
// Add what's left.
expanded.append(propValue.substring(ctoken));
return expanded.toString();
}
/**
* Gets the configuration property.
*
* @param name The name of the property to get.
*
* @throws ParseException if the property is wrongly formatted.
*
* @return Configuration property including any expansion/replacement
*/
public String getProperty(final String name)
throws ParseException
{
if (name == null) {
return null;
}
return expandProperty(configurationProperties.getProperty(PREFIX + name));
}
/**
* Gets the configuration property array.
* <p>
* Property array is constructed form the list of properties
* which end with {@code [index]}
* </p>
* <pre>
* daemon.arg[0] = argument 1
* daemon.arg[1] = argument 2
* daemon.arg[2] = argument 3
* </pre>
* @param name The name of the property array to get.
*
* @throws ParseException if the property is wrongly formatted.
*
* @return Configuration property array including any expansion/replacement
*/
public String[] getPropertyArray(final String name)
throws ParseException
{
final ArrayList<String> list = new ArrayList<>();
String args;
// Load daemon.arg[0] ... daemon.arg[n] into the String array.
//
while ((args = getProperty(name + "[" + list.size() + "]")) != null) {
list.add(args);
}
return list.toArray(EMPTY_STRING_ARRAY);
}
}