001/*
002 *  Copyright 2012 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.runtime.config;
017
018import java.io.File;
019import java.util.Date;
020import java.util.HashMap;
021import java.util.Map;
022
023import javax.xml.parsers.SAXParserFactory;
024
025import org.slf4j.Logger;
026import org.slf4j.LoggerFactory;
027
028import org.ametys.runtime.parameter.ParameterHelper;
029import org.ametys.runtime.parameter.ParameterHelper.ParameterType;
030import org.ametys.runtime.util.MapHandler;
031
032
033/**
034 * Config bean <br>
035 * Reads/Write config file. <br>
036 */
037public final class Config
038{
039    // Logger for traces
040    private static Logger _logger = LoggerFactory.getLogger(Config.class);
041    
042    // config file
043    private static String __filename;
044    
045    // true if the config file does exist
046    private static boolean __fileExists;
047    
048    // shared instance for optimization
049    private static Config __config;
050    
051    // Initialization status
052    private static boolean _initialized;
053
054    // The last modification date
055    private static long __lastModified = -1;
056    
057    // Typed value (filled after read)
058    private Map<String, String> _values;
059
060    private Config() throws Exception
061    {
062        if (_logger.isInfoEnabled())
063        {
064            _logger.info("Loading configuration values from file");
065        }
066        
067        _values = read();
068    }
069
070    /**
071     * Get the instance of Config using the config file.<br>
072     * @return An instance of Config containing config file value, or null if config file cannot be read.
073     */
074    public static Config getInstance()
075    {
076        if (!_initialized)
077        {
078            return null;
079        }
080        
081        if (__config == null)
082        {
083            try
084            {
085                __config = new Config();
086            }
087            catch (Exception e)
088            {
089                if (_logger.isWarnEnabled())
090                {
091                    _logger.warn("Exception creating Config, it won't be accessible.", e);
092                }
093                
094                return null;
095            }
096        }
097        else if (__fileExists && new File(__filename).exists() && __lastModified < new File(__filename).lastModified())
098        {
099            try
100            {
101                _logger.info("The config file has changed. Let's reload."); 
102                __config._values = read();
103            }
104            catch (Exception e)
105            {
106                // __lastModified was changed, so we will not fail several times
107                // _values was not modified
108                // __fileExists is still true
109                _logger.error("The config file '" + __filename + "' was modified but could not be reloaded due to an exception", e);
110            }
111        }
112        
113        return __config;
114    }
115    
116    /**
117     * Dispose this Config instance
118     */
119    public static void dispose()
120    {
121        __config = null;
122    }
123
124    /**
125     * Set the config filename
126     * @param filename Name with path of the config file
127     */
128    public static void setFilename(String filename)
129    {
130        __filename = filename;
131        __fileExists = new File(__filename).exists();
132    }
133    
134    /**
135     * Set the initialization status of the configuration
136     * @param initialized the initialization status of the configuration
137     */
138    public static void setInitialized(boolean initialized)
139    {
140        _initialized = initialized;
141    }
142    
143    /**
144     * Returns true if the config filename exists.
145     * @return true if the config filename exists.
146     */
147    public static boolean getFileExists()
148    {
149        return __fileExists;
150    }
151
152    /**
153     * Return the typed value as String
154     * @param id Id of the parameter to get
155     * @return the typed value as String
156     */
157    public String getValueAsString(String id)
158    {
159        return _values.get(id);
160    }
161
162    /**
163     * Return the typed value as Date
164     * @param id Id of the parameter to get
165     * @return the typed value as Date
166     */
167    public Date getValueAsDate(String id)
168    {
169        String value = _values.get(id);
170        
171        return (Date) ParameterHelper.castValue(value, ParameterType.DATE);
172    }
173
174    /**
175     * Return the typed value as long
176     * @param id Id of the parameter to get
177     * @return the typed value as long
178     */
179    public Long getValueAsLong(String id)
180    {
181        String value = _values.get(id);
182        
183        return (Long) ParameterHelper.castValue(value, ParameterType.LONG);
184    }
185
186    /**
187     * Return the typed value as boolean
188     * @param id Id of the parameter to get
189     * @return the typed value as boolean
190     */
191    public Boolean getValueAsBoolean(String id)
192    {
193        String value = _values.get(id);
194        
195        return (Boolean) ParameterHelper.castValue(value, ParameterType.BOOLEAN);
196    }
197
198    /**
199     * Return the typed value casted in double
200     * @param id Id of the parameter to get
201     * @return the typed value casted in double
202     */
203    public Double getValueAsDouble(String id)
204    {
205        String value = _values.get(id);
206        
207        return (Double) ParameterHelper.castValue(value, ParameterType.DOUBLE);
208    }
209    
210    /**
211     * Read config file and get untyped values (String object)
212     * @return Map (key, untyped value) representing the config file
213     * @throws Exception if a problem occurs reading values
214     */
215    public static Map<String, String> read() throws Exception
216    {
217        Map<String, String> configValues = new HashMap<>();
218
219        // Lit le fichier de déploiement pour déterminer les valeurs non typées
220        File configFile = new File(__filename);
221        
222        __fileExists = configFile.exists();
223        if (__fileExists)
224        {
225            __lastModified  = configFile.lastModified();
226            SAXParserFactory.newInstance().newSAXParser().parse(configFile, new MapHandler(configValues));
227        }
228        else
229        {
230            __lastModified = -1;
231        }
232
233        return configValues;
234    }
235}