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 1.0 <i>(SVN $Revision: 925053 $)</i>
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         * Load 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            try {
087                if (fileName == null)
088                    fileName = DEFAULT_CONFIG;
089                FileInputStream file = new FileInputStream(fileName);
090                configurationProperties.clear();
091                configurationProperties.load(file);
092                ok = true;
093            }
094            catch (FileNotFoundException ex) {
095                // fileName does not exist
096            }
097            catch (IOException ex) {
098                // Error reading properties file
099            }
100            return ok;
101        }
102    
103        private String expandProperty(String propValue)
104            throws ParseException
105        {
106            StringBuffer expanded;
107            int btoken;
108            int ctoken = 0;
109    
110            if (propValue == null)
111                return null;
112            expanded = new StringBuffer();
113            btoken   = propValue.indexOf(BTOKEN);
114            while (btoken != -1) {
115                if (btoken > 0 && propValue.charAt(btoken - 1) == BTOKEN.charAt(0)) {
116                    // Skip and unquote.
117                    expanded.append(propValue.substring(ctoken, btoken));
118                    ctoken = btoken + 1;
119                    btoken = propValue.indexOf(BTOKEN, btoken + BTOKEN.length());
120                    continue;
121                }
122                int etoken = propValue.indexOf(ETOKEN, btoken);
123                if (etoken != -1) {
124                    String variable = propValue.substring(btoken + BTOKEN.length(), etoken);
125                    String sysvalue = systemProperties.getProperty(variable);
126                    if (sysvalue == null) {
127                        // Try with the environment if there was no
128                        // property by that name.
129                        sysvalue = System.getenv(variable);
130                    }
131                    if (sysvalue != null) {
132                        String strtoken = propValue.substring(ctoken, btoken);
133                        expanded.append(strtoken);
134                        expanded.append(sysvalue);
135                        ctoken = etoken + ETOKEN.length();
136                    }
137                }
138                else {
139                    // We have "${" without "}"
140                    throw new ParseException("Error while looking for teminating '" +
141                                             ETOKEN + "'", btoken);
142                }
143                btoken = propValue.indexOf(BTOKEN, etoken + ETOKEN.length());
144            }
145            // Add what's left.
146            expanded.append(propValue.substring(ctoken, propValue.length()));
147            return expanded.toString();
148        }
149    
150        /**
151         * Get the configuration property.
152         * @param name The name of the property to get.
153         *
154         * @throws ParseException if the property is wrongly formatted.
155         */
156        public String getProperty(String name)
157            throws ParseException
158        {
159            if (name == null)
160                return null;
161            else
162                return expandProperty(configurationProperties.getProperty(PREFIX + name));
163        }
164    
165        /**
166         * Get the configuration property array.
167         * <p>
168         * Property array is constructed form the lsit of properties
169         * which end with <code>[index]</code>
170         * </p>
171         * <pre>
172         * daemon.arg[0] = argument 1
173         * daemon.arg[1] = argument 2
174         * daemon.arg[2] = argument 3
175         * </pre>
176         * @param name The name of the property array to get.
177         *
178         * @throws ParseException if the property is wrongly formatted.
179         */
180        public String[] getPropertyArray(String name)
181            throws ParseException
182        {
183            ArrayList list = new ArrayList();
184            String    args;
185    
186            // Load daemon.arg[0] ... daemon.arg[n] into the String array.
187            //
188            while ((args = getProperty(name + "[" + list.size() + "]")) != null) {
189                list.add(args);
190            }
191            return (String[])list.toArray(new String[list.size()]);
192        }
193    
194    }
195