James Moger
2013-01-16 8c8f1f537b62a608e9ef01b70bec5a8df4dc8e8a
commit | author | age
f13c4c 1 /*
JM 2  * Copyright 2011 gitblit.com.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
5fe7df 16 package com.gitblit;
JM 17
18 import java.io.File;
19 import java.io.FileInputStream;
20 import java.io.FileNotFoundException;
97a20e 21 import java.util.Map;
5fe7df 22 import java.util.Properties;
97a20e 23
JM 24 import com.gitblit.utils.FileUtils;
5fe7df 25
JM 26 /**
892570 27  * Dynamically loads and reloads a properties file by keeping track of the last
JM 28  * modification date.
29  * 
30  * @author James Moger
5fe7df 31  * 
JM 32  */
f339f5 33 public class FileSettings extends IStoredSettings {
2a7306 34
8c9a20 35     protected final File propertiesFile;
db653a 36
f339f5 37     private final Properties properties = new Properties();
5fe7df 38
892570 39     private volatile long lastModified;
5e58f0 40     
JM 41     private volatile boolean forceReload;
5fe7df 42
28d6b2 43     public FileSettings(String file) {
f339f5 44         super(FileSettings.class);
28d6b2 45         this.propertiesFile = new File(file);
JM 46     }
db653a 47
892570 48     /**
JM 49      * Returns a properties object which contains the most recent contents of
50      * the properties file.
51      */
87cc1e 52     @Override
f339f5 53     protected synchronized Properties read() {
5e58f0 54         if (propertiesFile.exists() && (forceReload || (propertiesFile.lastModified() > lastModified))) {
2a7306 55             FileInputStream is = null;
5fe7df 56             try {
f339f5 57                 Properties props = new Properties();
28d6b2 58                 is = new FileInputStream(propertiesFile);
f339f5 59                 props.load(is);
85c2e6 60
f339f5 61                 // load properties after we have successfully read file
JM 62                 properties.clear();
63                 properties.putAll(props);
892570 64                 lastModified = propertiesFile.lastModified();
5e58f0 65                 forceReload = false;
5fe7df 66             } catch (FileNotFoundException f) {
2a7306 67                 // IGNORE - won't happen because file.exists() check above
5fe7df 68             } catch (Throwable t) {
f339f5 69                 logger.error("Failed to read " + propertiesFile.getName(), t);
2a7306 70             } finally {
JM 71                 if (is != null) {
72                     try {
73                         is.close();
74                     } catch (Throwable t) {
75                         // IGNORE
76                     }
77                 }
5fe7df 78             }
JM 79         }
80         return properties;
81     }
2a7306 82
892570 83     /**
97a20e 84      * Updates the specified settings in the settings file.
JM 85      */
86     public synchronized boolean saveSettings(Map<String, String> settings) {
87         String content = FileUtils.readContent(propertiesFile, "\n");
88         for (Map.Entry<String, String> setting:settings.entrySet()) {
89             String regex = "(?m)^(" + regExEscape(setting.getKey()) + "\\s*+=\\s*+)"
f3ff37 90                     + "(?:[^\r\n\\\\]++|\\\\(?:\r?\n|\r|.))*+$";
JM 91             String oldContent = content;
97a20e 92             content = content.replaceAll(regex, setting.getKey() + " = " + setting.getValue());
f3ff37 93             if (content.equals(oldContent)) {
JM 94                 // did not replace value because it does not exist in the file
95                 // append new setting to content (issue-85)
96                 content += "\n" + setting.getKey() + " = " + setting.getValue();
97             }
97a20e 98         }
JM 99         FileUtils.writeContent(propertiesFile, content);
5e58f0 100         // manually set the forceReload flag because not all JVMs support real
JM 101         // millisecond resolution of lastModified. (issue-55)        
102         forceReload = true;
97a20e 103         return true;
JM 104     }
105     
106     private String regExEscape(String input) {
8c8f1f 107         return input.replace(".", "\\.").replace("$", "\\$").replace("{", "\\{");
97a20e 108     }
JM 109
110     /**
892570 111      * @return the last modification date of the properties file
JM 112      */
113     protected long lastModified() {
114         return lastModified;
85c2e6 115     }
JM 116
5e58f0 117     /**
JM 118      * @return the state of the force reload flag
119      */
120     protected boolean forceReload() {
121         return forceReload;
122     }
123
87cc1e 124     @Override
JM 125     public String toString() {
28d6b2 126         return propertiesFile.getAbsolutePath();
87cc1e 127     }
5fe7df 128 }