View Javadoc

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  			// minimal syncronized block
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 		//no reload necessary here, it is done on the factory level. More flexibe that way, which is
107 		//if we were to create configuration manager we can control when to load the configurations...reload();
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 }