001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.daemon.support; 019 020import java.io.FileInputStream; 021import java.io.IOException; 022import java.util.ArrayList; 023import java.util.Properties; 024import java.text.ParseException; 025 026/** 027 * Used by jsvc for Daemon configuration. 028 * <p> 029 * Configuration is read from properties file. 030 * If no properties file is given the {@code daemon.properties} 031 * is used from the current directory. 032 * </p> 033 * <p> 034 * The properties file can have property values expanded at runtime 035 * by using System properties or execution environment. The part 036 * of the property value between {@code ${} and {@code }} 037 * will be used as System property or environment key. If found then 038 * the entire {@code ${foo}} will be replaced by the value of 039 * either system property or environment variable named {@code foo}. 040 * </p> 041 * <p> 042 * If no variable is found the {@code ${foo}} will be passed as is. 043 * In case of {@code $${foo}} this will be unescaped and resulting 044 * value will be {@code ${foo}}. 045 * </p> 046 * 047 */ 048public final class DaemonConfiguration 049{ 050 /** 051 * Default configuration file name. 052 */ 053 protected final static String DEFAULT_CONFIG = "daemon.properties"; 054 /** 055 * Property prefix 056 */ 057 protected final static String PREFIX = "daemon."; 058 private final static String BTOKEN = "${"; 059 private final static String ETOKEN = "}"; 060 061 062 private final Properties configurationProperties; 063 private final Properties systemProperties; 064 065 /** 066 * An empty immutable {@code String} array. 067 */ 068 static final String[] EMPTY_STRING_ARRAY = {}; 069 070 /** 071 * Default constructor 072 */ 073 public DaemonConfiguration() 074 { 075 configurationProperties = new Properties(); 076 systemProperties = System.getProperties(); 077 } 078 079 /** 080 * Loads the configuration properties file. 081 * 082 * @param fileName The properties file to load. 083 * @return {@code true} if the file was loaded. 084 */ 085 public boolean load(String fileName) 086 { 087 boolean ok = false; 088 FileInputStream file = null; 089 try { 090 if (fileName == null) { 091 fileName = DEFAULT_CONFIG; 092 } 093 file = new FileInputStream(fileName); 094 configurationProperties.clear(); 095 configurationProperties.load(file); 096 ok = true; 097 } 098 catch (final IOException ex) { 099 // Error reading properties file 100 } finally { 101 try { 102 if (file != null) { 103 file.close(); 104 } 105 } catch (final IOException ex) { 106 } 107 } 108 return ok; 109 } 110 111 private String expandProperty(final String propValue) 112 throws ParseException 113 { 114 final StringBuilder expanded; 115 int btoken; 116 int ctoken = 0; 117 118 if (propValue == null) { 119 return null; 120 } 121 expanded = new StringBuilder(); 122 btoken = propValue.indexOf(BTOKEN); 123 while (btoken != -1) { 124 if (btoken > 0 && propValue.charAt(btoken - 1) == BTOKEN.charAt(0)) { 125 // Skip and unquote. 126 expanded.append(propValue.substring(ctoken, btoken)); 127 ctoken = btoken + 1; 128 btoken = propValue.indexOf(BTOKEN, btoken + BTOKEN.length()); 129 continue; 130 } 131 final int etoken = propValue.indexOf(ETOKEN, btoken); 132 if (etoken == -1) { 133 // We have "${" without "}" 134 throw new ParseException("Error while looking for teminating '" + 135 ETOKEN + "'", btoken); 136 } 137 final String variable = propValue.substring(btoken + BTOKEN.length(), etoken); 138 String sysvalue = systemProperties.getProperty(variable); 139 if (sysvalue == null) { 140 // Try with the environment if there was no 141 // property by that name. 142 sysvalue = System.getenv(variable); 143 } 144 if (sysvalue != null) { 145 final String strtoken = propValue.substring(ctoken, btoken); 146 expanded.append(strtoken); 147 expanded.append(sysvalue); 148 ctoken = etoken + ETOKEN.length(); 149 } 150 btoken = propValue.indexOf(BTOKEN, etoken + ETOKEN.length()); 151 } 152 // Add what's left. 153 expanded.append(propValue.substring(ctoken)); 154 return expanded.toString(); 155 } 156 157 /** 158 * Gets the configuration property. 159 * 160 * @param name The name of the property to get. 161 * 162 * @throws ParseException if the property is wrongly formatted. 163 * 164 * @return Configuration property including any expansion/replacement 165 */ 166 public String getProperty(final String name) 167 throws ParseException 168 { 169 if (name == null) { 170 return null; 171 } 172 return expandProperty(configurationProperties.getProperty(PREFIX + name)); 173 } 174 175 /** 176 * Gets the configuration property array. 177 * <p> 178 * Property array is constructed form the list of properties 179 * which end with {@code [index]} 180 * </p> 181 * <pre> 182 * daemon.arg[0] = argument 1 183 * daemon.arg[1] = argument 2 184 * daemon.arg[2] = argument 3 185 * </pre> 186 * @param name The name of the property array to get. 187 * 188 * @throws ParseException if the property is wrongly formatted. 189 * 190 * @return Configuration property array including any expansion/replacement 191 */ 192 public String[] getPropertyArray(final String name) 193 throws ParseException 194 { 195 final ArrayList<String> list = new ArrayList<>(); 196 String args; 197 198 // Load daemon.arg[0] ... daemon.arg[n] into the String array. 199 // 200 while ((args = getProperty(name + "[" + list.size() + "]")) != null) { 201 list.add(args); 202 } 203 return list.toArray(EMPTY_STRING_ARRAY); 204 } 205} 206