1 package org.naftulin.configmgr;
2
3 import java.util.Collections;
4 import java.util.HashMap;
5 import java.util.Iterator;
6 import java.util.LinkedList;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.Set;
10 import java.util.Map.Entry;
11
12 import org.apache.log4j.Logger;
13 import org.naftulin.configmgr.parsers.ConfigurationRecordsToEntriesConverter;
14
15 /***
16 * Configuration Management provides a convinient way to manage configuration for your programs.
17 * Alows to load and reload configurations, add configuration, list all the configurations and
18 * returns configuration manager interface. Configuration manager interface returns configuration
19 * based on the key.
20 *
21 * @author Henry Naftulin
22 * @since 1.0
23 */
24 public class ConfigurationManagementImpl implements ConfigurationManagement {
25 private final class ConfigurationManagerImpl implements ConfigurationManager {
26 /***
27 * Returns the configuration based on the key.
28 * If the configuration is not found or is in error state throws an exception.
29 * @param key configuration mapped under.
30 * @return the configuration based on the key.
31 * @throws ConfigurationManagerException if configuration is not found or in error state.
32 */
33 public Object getConfiguration(final String key) throws ConfigurationManagerException {
34 ConfigurationManagementEntry entry;
35 synchronized (lock) {
36 if (configuration.containsKey(key)) {
37 entry = configuration.get(key);
38 if (! entry.isError()) {
39 return entry.getContent();
40 }
41 throw new ConfigurationManagerException("Entry was found, but it is in error state. " + entry.getContent());
42 }
43 }
44 log.warn("no entry for " + key + " is found in the configuration manager");
45 throw new EntryNotFoundException("no entry for " + key + " is found.");
46 }
47
48 /***
49 * Returns the configuration based on the key if found and not in error state. Otherwise
50 * returns null.
51 * @param key configuration mapped under.
52 * @return conifguration if found and it does not countain errors, null otherwise
53 */
54 public Object getConfigurationSilent(final String key) {
55 boolean entryFound = false;
56 ConfigurationManagementEntry entry = null;
57 Object retObject = null;
58
59 synchronized (lock) {
60 entryFound = configuration.containsKey(key);
61 if (entryFound) {
62 entry = configuration.get(key);
63 if (!entry.isError()) {
64 retObject = entry.getContent();
65 }
66 }
67 }
68
69 if (!entryFound) {
70 log.warn("no entry for " + key + " is found in the configuration manager");
71 }
72 return retObject;
73 }
74
75
76 }
77
78 private static final Logger log = Logger.getLogger(ConfigurationManagementImpl.class);
79
80 /***
81 * Anonymous private class that represents configuration manager interface.
82 */
83 private final ConfigurationManager manager;
84 private final Object lock;
85 private String masterRecordFileName;
86 private Map<String, ConfigurationManagementEntry> configuration = new HashMap<String, ConfigurationManagementEntry>();
87 private boolean intialized;
88
89
90 /***
91 * Creates a configuration manager without any configurations. Needed only for the unit testing.
92 */
93 ConfigurationManagementImpl() {
94 lock = new Object();
95 manager = new ConfigurationManagerImpl();
96 }
97
98 /***
99 * Creates a configuration management engine given a master configuration file.
100 * @param masterRecordFileName a file that describes configuration for configuration manager
101 * @throws ConfigurationManagerException if master configuration file cannot be read or contains an error.
102 */
103 ConfigurationManagementImpl(String masterRecordFileName) throws ConfigurationManagerException {
104 this();
105 this.masterRecordFileName = masterRecordFileName;
106
107
108 }
109
110 /***
111 * Reloades configurations stored in the configuration management engine. The
112 * configurations are read again, parsed and stored.
113 * @throws ConfigurationManagerException if a problem occurs while reading or parsing the configurations.
114 */
115 public void reload() throws ConfigurationManagerException {
116 final Map<String, ConfigurationManagementEntry> newConfig = readConfiguration();
117 synchronized (lock) {
118 this.configuration = newConfig;
119 this.intialized = true;
120 }
121
122 }
123
124 /***
125 * Returns {@link ConfigurationManagementEntry configuration management entry} based on the key passed.
126 * @param key key the entry is stored under
127 * @return configuration management entry based on the key passed
128 * @throws ConfigurationManagerException if the entry is not found.
129 */
130 public ConfigurationManagementEntry getConfigurationManagmentEntry(final String key) throws ConfigurationManagerException {
131 boolean entryFound = false;
132 ConfigurationManagementEntry entry = null;
133 synchronized (lock) {
134 entryFound = configuration.containsKey(key);
135 entry = configuration.get(key);
136 }
137 if (!entryFound) {
138 throw new EntryNotFoundException("Entry identified by key "+ key + " was not found");
139 }
140 return entry;
141 }
142
143 /***
144 * Returns a list of {@link ConfigurationManagementEntry configuration management entries} managed by this configuration managment engine.
145 * @return a list of {@link ConfigurationManagementEntry configuration management entries} managed by this configuration managment engine.
146 */
147 public List<ConfigurationManagementEntry> getConfigurationManagmentEntries() {
148 final List<ConfigurationManagementEntry> entries = new LinkedList<ConfigurationManagementEntry>();
149 Set<Entry<String, ConfigurationManagementEntry>> set = null;
150 synchronized (lock) {
151 set = Collections.unmodifiableSet(this.configuration.entrySet());
152 }
153 if (set != null) {
154 Iterator<Entry<String, ConfigurationManagementEntry>> it = set.iterator();
155 while(it.hasNext()) {
156 Entry<String, ConfigurationManagementEntry> mapEntry = it.next();
157 entries.add((ConfigurationManagementEntry) mapEntry.getValue());
158 }
159 }
160 return Collections.unmodifiableList(entries);
161 }
162
163 /***
164 * Returns true if configuration Management has loaded configurations. Returns false
165 * before that point. After instantiation, we can confgure where configuration master
166 * record is read from. Once the master record is configured, we will be able to reload
167 * configuration manager. Reloading configuration manager will load all configurations,
168 * and the flag will switch to initialized.
169 * @return true if configuration Management has loaded configurations.
170 */
171 public boolean isInitialized() {
172 synchronized (lock) {
173 return intialized;
174 }
175 }
176
177 /***
178 * Returns a map with {@link ConfigurationManagementEntry configuration management entries} keyed by confguration entry key.
179 * Reads and parses all configuration entries described in master confiuration file.
180 * @return a map with {@link ConfigurationManagementEntry configuration management entries} keyed by confguration entry key.
181 * @throws ConfigurationManagerException if a configuration cannot be read or parsed.
182 */
183 private Map<String, ConfigurationManagementEntry> readConfiguration() throws ConfigurationManagerException {
184 final Map<String, ConfigurationManagementEntry> newConfiguration = new HashMap<String, ConfigurationManagementEntry>();
185 if (masterRecordFileName != null) {
186 final ConfigurationRecordsToEntriesConverter converter = new ConfigurationRecordsToEntriesConverter();
187 final ConfigurationManagementEntry[] entries = converter.createConfigurationEntriesFromConfigurationFile(masterRecordFileName);
188 for(int i=0; i < entries.length; ++i) {
189 addConfigurationManagmentEntry(newConfiguration, entries[i]);
190 }
191 }
192 return newConfiguration;
193 }
194
195 /***
196 * Returns configuration manager created by the configuration management engine.
197 * @return configuration manager.
198 */
199 public ConfigurationManager getConfigurationManager() {
200 synchronized(lock) {
201 return this.manager;
202 }
203 }
204
205 /***
206 * Adds a configuration entry to the configuration manager. Needed to add ad-hoc
207 * configurations to the configuration manager. when adding a new configuration with the same key
208 * old configuration should be replaced. Is overwritten flag for the new
209 * configuration should be set. Delegates to private method that takes
210 * a map to which the configuration is added.
211 * @param entry configuration entry
212 * @exception if the entry is in error
213 */
214 public void addConfigurationManagmentEntry(final ConfigurationManagementEntry entry) throws EntryInErrorException {
215 addConfigurationManagmentEntry(this.configuration, entry);
216 }
217
218 /***
219 * Adds a configuration entry to the configuration manager. Needed to add ad-hoc
220 * configurations to the configuration manager. when adding a new configuration with the same key
221 * old configuration should be replaced. Is overwritten flag for the new
222 * configuration should be set.
223 * @param toConfiguration a configuration to which an entry is added
224 * @param entry configuration entry
225 * @exception if the entry is in error
226 */
227 private void addConfigurationManagmentEntry(final Map<String, ConfigurationManagementEntry> toConfiguration,
228 final ConfigurationManagementEntry entry) throws EntryInErrorException {
229 if (entry.getKey() == null) {
230 throw new EntryInErrorException("Entry key should not be null. Entry is " + entry);
231 }
232 synchronized(lock) {
233 final Object obj = toConfiguration.put(entry.getKey(), entry);
234 entry.setOverwritten(obj != null);
235 }
236 }
237 }