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    
018    package org.apache.commons.daemon.support;
019    
020    import java.io.FileInputStream;
021    import java.io.FileNotFoundException;
022    import java.io.IOException;
023    import java.util.ArrayList;
024    import java.util.Properties;
025    import 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 1299939 2012-03-13 01:02:23Z sebb $
049     * @author Mladen Turk
050     */
051    public 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 list 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