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.FileNotFoundException;
022import java.io.IOException;
023import java.util.ArrayList;
024import java.util.Properties;
025import java.text.ParseException;
026
027/**
028 * Used by jsvc for Daemon configuration.
029 * <p>
030 * Configuration is read from properties file.
031 * If no properties file is given the <code>daemon.properties</code>
032 * is used from the current directory.
033 * </p>
034 * <p>
035 * The properties file can have property values expanded at runtime
036 * by using System properties or execution environment. The part
037 * of the property value between <code>${</code> and <code>}</code>
038 * will be used as System property or environment key. If found then
039 * the entire <code>${foo}</code> will be replaced by the value of
040 * either system property or environment variable named <code>foo</code>.
041 * </p>
042 * <p>
043 * If no variable is found the <code>${foo}</code>  will be passed as is.
044 * In case of <code>$${foo}</code> this will be unescaped and resulting
045 * value will be <code>${foo}</code>.
046 * </p>
047 *
048 * @version $Id: DaemonConfiguration.java 1204010 2011-11-19 16:15:23Z ggregory $
049 * @author Mladen Turk
050 */
051public final class DaemonConfiguration
052{
053    /**
054     * Default configuration file name.
055     */
056    protected final static String DEFAULT_CONFIG        = "daemon.properties";
057    /**
058     * Property prefix
059     */
060    protected final static String PREFIX                = "daemon.";
061    private   final static String BTOKEN                = "${";
062    private   final static String ETOKEN                = "}";
063
064
065    private final Properties configurationProperties;
066    private final Properties systemProperties;
067
068    /**
069     * Default constructor
070     */
071    public DaemonConfiguration()
072    {
073        configurationProperties = new Properties();
074        systemProperties        = System.getProperties();
075    }
076
077    /**
078     * Loads the configuration properties file.
079     *
080     * @param fileName The properties file to load.
081     * @return <code>true</code> if the file was loaded.
082     */
083    public boolean load(String fileName)
084    {
085        boolean ok = false;
086        FileInputStream file = null;
087        try {
088            if (fileName == null)
089                fileName = DEFAULT_CONFIG;
090            file = new FileInputStream(fileName);
091            configurationProperties.clear();
092            configurationProperties.load(file);
093            ok = true;
094        }
095        catch (FileNotFoundException ex) {
096            // fileName does not exist
097        }
098        catch (IOException ex) {
099            // Error reading properties file
100        } finally {
101            try {
102                if (file != null)
103                    file.close();
104            } catch (IOException ex) {
105            }
106        }
107        return ok;
108    }
109
110    private String expandProperty(String propValue)
111        throws ParseException
112    {
113        StringBuffer expanded;
114        int btoken;
115        int ctoken = 0;
116
117        if (propValue == null)
118            return null;
119        expanded = new StringBuffer();
120        btoken   = propValue.indexOf(BTOKEN);
121        while (btoken != -1) {
122            if (btoken > 0 && propValue.charAt(btoken - 1) == BTOKEN.charAt(0)) {
123                // Skip and unquote.
124                expanded.append(propValue.substring(ctoken, btoken));
125                ctoken = btoken + 1;
126                btoken = propValue.indexOf(BTOKEN, btoken + BTOKEN.length());
127                continue;
128            }
129            int etoken = propValue.indexOf(ETOKEN, btoken);
130            if (etoken != -1) {
131                String variable = propValue.substring(btoken + BTOKEN.length(), etoken);
132                String sysvalue = systemProperties.getProperty(variable);
133                if (sysvalue == null) {
134                    // Try with the environment if there was no
135                    // property by that name.
136                    sysvalue = System.getenv(variable); // N.B. Deprecated in Java 1.3/1.4, but re-instated in Java 1.5+
137                }
138                if (sysvalue != null) {
139                    String strtoken = propValue.substring(ctoken, btoken);
140                    expanded.append(strtoken);
141                    expanded.append(sysvalue);
142                    ctoken = etoken + ETOKEN.length();
143                }
144            }
145            else {
146                // We have "${" without "}"
147                throw new ParseException("Error while looking for teminating '" +
148                                         ETOKEN + "'", btoken);
149            }
150            btoken = propValue.indexOf(BTOKEN, etoken + ETOKEN.length());
151        }
152        // Add what's left.
153        expanded.append(propValue.substring(ctoken, propValue.length()));
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    public String getProperty(String name)
165        throws ParseException
166    {
167        if (name == null)
168            return null;
169        else
170            return expandProperty(configurationProperties.getProperty(PREFIX + name));
171    }
172
173    /**
174     * Gets the configuration property array.
175     * <p>
176     * Property array is constructed form the lsit of properties
177     * which end with <code>[index]</code>
178     * </p>
179     * <pre>
180     * daemon.arg[0] = argument 1
181     * daemon.arg[1] = argument 2
182     * daemon.arg[2] = argument 3
183     * </pre>
184     * @param name The name of the property array to get.
185     *
186     * @throws ParseException if the property is wrongly formatted.
187     */
188    public String[] getPropertyArray(String name)
189        throws ParseException
190    {
191        ArrayList list = new ArrayList();
192        String    args;
193
194        // Load daemon.arg[0] ... daemon.arg[n] into the String array.
195        //
196        while ((args = getProperty(name + "[" + list.size() + "]")) != null) {
197            list.add(args);
198        }
199        return (String[])list.toArray(new String[list.size()]);
200    }
201
202}
203