blob: 35ccc085477972fb63a51869e11006a786377970 [file] [log] [blame]
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001package net.floodlightcontroller.core.module;
2
3import java.io.File;
4import java.io.FileInputStream;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08005import java.util.ArrayList;
6import java.util.Arrays;
7import java.util.Collection;
8import java.util.Enumeration;
9import java.util.HashMap;
10import java.util.HashSet;
11import java.util.Iterator;
12import java.util.LinkedList;
13import java.util.Map;
14import java.util.Map.Entry;
15import java.util.Properties;
16import java.util.Queue;
17import java.util.ServiceConfigurationError;
18import java.util.ServiceLoader;
19import java.util.Set;
20
21import net.floodlightcontroller.core.annotations.LogMessageDoc;
22import net.floodlightcontroller.core.annotations.LogMessageDocs;
23
24import org.slf4j.Logger;
25import org.slf4j.LoggerFactory;
26
27/**
28 * Finds all Floodlight modules in the class path and loads/starts them.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080029 *
Ray Milkey269ffb92014-04-03 14:43:30 -070030 * @author alexreimers
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080031 */
32public class FloodlightModuleLoader {
Ray Milkey269ffb92014-04-03 14:43:30 -070033 protected final static Logger logger =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080034 LoggerFactory.getLogger(FloodlightModuleLoader.class);
35
36 protected static Map<Class<? extends IFloodlightService>,
Ray Milkey269ffb92014-04-03 14:43:30 -070037 Collection<IFloodlightModule>> serviceMap;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080038 protected static Map<IFloodlightModule,
Ray Milkey269ffb92014-04-03 14:43:30 -070039 Collection<Class<? extends
40 IFloodlightService>>> moduleServiceMap;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080041 protected static Map<String, IFloodlightModule> moduleNameMap;
42 protected static Object lock = new Object();
Ray Milkey269ffb92014-04-03 14:43:30 -070043
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080044 protected FloodlightModuleContext floodlightModuleContext;
Jonathan Hart04aba912014-03-27 15:05:48 -070045
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080046 public static final String FLOODLIGHT_MODULES_KEY =
47 "floodlight.modules";
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080048
Ray Milkey269ffb92014-04-03 14:43:30 -070049 public FloodlightModuleLoader() {
50 floodlightModuleContext = new FloodlightModuleContext();
51 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080052
Ray Milkey269ffb92014-04-03 14:43:30 -070053 /**
54 * Finds all IFloodlightModule(s) in the classpath. It creates 3 Maps.
55 * serviceMap -> Maps a service to a module
56 * moduleServiceMap -> Maps a module to all the services it provides
57 * moduleNameMap -> Maps the string name to the module
58 *
59 * @throws FloodlightModuleException If two modules are specified in the configuration
60 * that provide the same service.
61 */
62 protected static void findAllModules(Collection<String> mList) throws FloodlightModuleException {
63 synchronized (lock) {
64 if (serviceMap != null) return;
65 serviceMap =
66 new HashMap<Class<? extends IFloodlightService>,
67 Collection<IFloodlightModule>>();
68 moduleServiceMap =
69 new HashMap<IFloodlightModule,
70 Collection<Class<? extends
71 IFloodlightService>>>();
72 moduleNameMap = new HashMap<String, IFloodlightModule>();
73
74 // Get all the current modules in the classpath
75 ClassLoader cl = Thread.currentThread().getContextClassLoader();
76 ServiceLoader<IFloodlightModule> moduleLoader
77 = ServiceLoader.load(IFloodlightModule.class, cl);
78 // Iterate for each module, iterate through and add it's services
79 Iterator<IFloodlightModule> moduleIter = moduleLoader.iterator();
80 while (moduleIter.hasNext()) {
81 IFloodlightModule m = null;
82 try {
83 m = moduleIter.next();
84 } catch (ServiceConfigurationError sce) {
85 logger.debug("Could not find module: {}", sce.getMessage());
86 //moduleIter.remove();
87 continue;
88 }
89 //}
90 //for (IFloodlightModule m : moduleLoader) {
91 if (logger.isDebugEnabled()) {
92 logger.debug("Found module " + m.getClass().getName());
93 }
94
95 // Set up moduleNameMap
96 moduleNameMap.put(m.getClass().getCanonicalName(), m);
97
98 // Set up serviceMap
99 Collection<Class<? extends IFloodlightService>> servs =
100 m.getModuleServices();
101 if (servs != null) {
102 moduleServiceMap.put(m, servs);
103 for (Class<? extends IFloodlightService> s : servs) {
104 Collection<IFloodlightModule> mods =
105 serviceMap.get(s);
106 if (mods == null) {
107 mods = new ArrayList<IFloodlightModule>();
108 serviceMap.put(s, mods);
109 }
110 mods.add(m);
111 // Make sure they haven't specified duplicate modules in the config
112 int dupInConf = 0;
113 for (IFloodlightModule cMod : mods) {
114 if (mList.contains(cMod.getClass().getCanonicalName()))
115 dupInConf += 1;
116 }
117
118 if (dupInConf > 1) {
119 String duplicateMods = "";
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800120 for (IFloodlightModule mod : mods) {
121 duplicateMods += mod.getClass().getCanonicalName() + ", ";
122 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700123 throw new FloodlightModuleException("ERROR! The configuraiton" +
124 " file specifies more than one module that provides the service " +
125 s.getCanonicalName() + ". Please specify only ONE of the " +
126 "following modules in the config file: " + duplicateMods);
127 }
128 }
129 }
130 }
131 }
132 }
133
134 /**
135 * Loads the modules from a specified configuration file.
136 *
137 * @param fName The configuration file path
138 * @return An IFloodlightModuleContext with all the modules to be started
139 * @throws FloodlightModuleException
140 */
141 @LogMessageDocs({
142 @LogMessageDoc(level = "INFO",
143 message = "Loading modules from file {file name}",
144 explanation = "The controller is initializing its module " +
145 "configuration from the specified properties file"),
146 @LogMessageDoc(level = "INFO",
147 message = "Loading default modules",
148 explanation = "The controller is initializing its module " +
149 "configuration to the default configuration"),
150 @LogMessageDoc(level = "ERROR",
151 message = "Could not load module configuration file",
152 explanation = "The controller failed to read the " +
153 "module configuration file",
154 recommendation = "Verify that the module configuration is " +
155 "present. " + LogMessageDoc.CHECK_CONTROLLER),
156 @LogMessageDoc(level = "ERROR",
157 message = "Could not load default modules",
158 explanation = "The controller failed to read the default " +
159 "module configuration",
160 recommendation = LogMessageDoc.CHECK_CONTROLLER)
161 })
162 public IFloodlightModuleContext loadModulesFromConfig(String fName)
163 throws FloodlightModuleException {
164 Properties prop = new Properties();
165
166 File f = new File(fName);
167 if (f.isFile()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800168 logger.info("Loading modules from file {}", fName);
169 try {
170 prop.load(new FileInputStream(fName));
171 } catch (Exception e) {
172 logger.error("Could not load module configuration file", e);
173 System.exit(1);
174 }
175 } else {
Jonathan Hart04aba912014-03-27 15:05:48 -0700176 logger.error("No configuration file specified");
177 System.exit(1);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800178 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700179
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800180 String moduleList = prop.getProperty(FLOODLIGHT_MODULES_KEY)
Ray Milkey269ffb92014-04-03 14:43:30 -0700181 .replaceAll("\\s", "");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800182 Collection<String> configMods = new ArrayList<String>();
183 configMods.addAll(Arrays.asList(moduleList.split(",")));
184 return loadModulesFromList(configMods, prop);
Ray Milkey269ffb92014-04-03 14:43:30 -0700185 }
186
187 /**
188 * Loads modules (and their dependencies) specified in the list
189 *
190 * @param mList The array of fully qualified module names
191 * @param ignoreList The list of Floodlight services NOT to
192 * load modules for. Used for unit testing.
193 * @return The ModuleContext containing all the loaded modules
194 * @throws FloodlightModuleException
195 */
196 protected IFloodlightModuleContext loadModulesFromList(Collection<String> configMods, Properties prop,
197 Collection<IFloodlightService> ignoreList) throws FloodlightModuleException {
198 logger.debug("Starting module loader");
199 if (logger.isDebugEnabled() && ignoreList != null)
200 logger.debug("Not loading module services " + ignoreList.toString());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800201
202 findAllModules(configMods);
Ray Milkey269ffb92014-04-03 14:43:30 -0700203
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800204 Collection<IFloodlightModule> moduleSet = new ArrayList<IFloodlightModule>();
205 Map<Class<? extends IFloodlightService>, IFloodlightModule> moduleMap =
206 new HashMap<Class<? extends IFloodlightService>,
Ray Milkey269ffb92014-04-03 14:43:30 -0700207 IFloodlightModule>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800208
209 Queue<String> moduleQ = new LinkedList<String>();
210 // Add the explicitly configured modules to the q
211 moduleQ.addAll(configMods);
212 Set<String> modsVisited = new HashSet<String>();
Ray Milkey269ffb92014-04-03 14:43:30 -0700213
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800214 while (!moduleQ.isEmpty()) {
215 String moduleName = moduleQ.remove();
216 if (modsVisited.contains(moduleName))
217 continue;
218 modsVisited.add(moduleName);
219 IFloodlightModule module = moduleNameMap.get(moduleName);
220 if (module == null) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700221 throw new FloodlightModuleException("Module " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800222 moduleName + " not found");
223 }
224 // If the module provies a service that is in the
225 // services ignorelist don't load it.
226 if ((ignoreList != null) && (module.getModuleServices() != null)) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700227 for (IFloodlightService ifs : ignoreList) {
228 for (Class<?> intsIgnore : ifs.getClass().getInterfaces()) {
229 //System.out.println(intsIgnore.getName());
230 // Check that the interface extends IFloodlightService
231 //if (intsIgnore.isAssignableFrom(IFloodlightService.class)) {
232 //System.out.println(module.getClass().getName());
233 if (intsIgnore.isAssignableFrom(module.getClass())) {
234 // We now ignore loading this module.
235 logger.debug("Not loading module " +
236 module.getClass().getCanonicalName() +
237 " because interface " +
238 intsIgnore.getCanonicalName() +
239 " is in the ignore list.");
240
241 continue;
242 }
243 //}
244 }
245 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800246 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700247
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800248 // Add the module to be loaded
249 addModule(moduleMap, moduleSet, module);
250 // Add it's dep's to the queue
Ray Milkey269ffb92014-04-03 14:43:30 -0700251 Collection<Class<? extends IFloodlightService>> deps =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800252 module.getModuleDependencies();
253 if (deps != null) {
254 for (Class<? extends IFloodlightService> c : deps) {
255 IFloodlightModule m = moduleMap.get(c);
256 if (m == null) {
257 Collection<IFloodlightModule> mods = serviceMap.get(c);
258 // Make sure only one module is loaded
259 if ((mods == null) || (mods.size() == 0)) {
260 throw new FloodlightModuleException("ERROR! Could not " +
261 "find an IFloodlightModule that provides service " +
262 c.toString());
263 } else if (mods.size() == 1) {
264 IFloodlightModule mod = mods.iterator().next();
265 if (!modsVisited.contains(mod.getClass().getCanonicalName()))
266 moduleQ.add(mod.getClass().getCanonicalName());
267 } else {
268 boolean found = false;
269 for (IFloodlightModule moduleDep : mods) {
270 if (configMods.contains(moduleDep.getClass().getCanonicalName())) {
271 // Module will be loaded, we can continue
272 found = true;
273 break;
274 }
275 }
276 if (!found) {
277 String duplicateMods = "";
278 for (IFloodlightModule mod : mods) {
279 duplicateMods += mod.getClass().getCanonicalName() + ", ";
280 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700281 throw new FloodlightModuleException("ERROR! Found more " +
282 "than one (" + mods.size() + ") IFloodlightModules that provides " +
283 "service " + c.toString() +
284 ". Please specify one of the following modules in the config: " +
285 duplicateMods);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800286 }
287 }
288 }
289 }
290 }
291 }
Pavlin Radoslavovc35229e2014-02-06 16:19:37 -0800292
Ray Milkey269ffb92014-04-03 14:43:30 -0700293 //
294 // Reorder the moduleSet to take into account the module dependencies:
295 // If a module depends on the service provided by another module, the
296 // latter should be included before the former.
297 //
298 Collection<IFloodlightModule> orderedModuleSet =
299 new ArrayList<IFloodlightModule>();
300 while (!moduleSet.isEmpty()) {
301 //
302 // Evaluate each module in the unsorted collection: if all its
303 // dependencies are in the orderedModuleSet, then add it to the
304 // orderedModuleSet.
305 //
306 boolean moduleWasSorted = false;
307 for (IFloodlightModule module : moduleSet) {
308 Collection<Class<? extends IFloodlightService>> deps =
309 module.getModuleDependencies();
310 boolean allDepsFound = true;
311 if (deps != null) {
312 for (Class<? extends IFloodlightService> c : deps) {
313 IFloodlightModule m = moduleMap.get(c);
314 // NOTE: Earlier we checked that the module exists
315 assert (m != null);
316 if (!orderedModuleSet.contains(m)) {
317 allDepsFound = false;
318 break;
319 }
320 }
321 }
Pavlin Radoslavovc35229e2014-02-06 16:19:37 -0800322
Ray Milkey269ffb92014-04-03 14:43:30 -0700323 // Move the module to the sorted collection
324 if (allDepsFound) {
325 orderedModuleSet.add(module);
326 moduleSet.remove(module);
327 moduleWasSorted = true;
328 break;
329 }
330 }
Pavlin Radoslavovc35229e2014-02-06 16:19:37 -0800331
Ray Milkey269ffb92014-04-03 14:43:30 -0700332 //
333 // Test for cyclic depenency:
334 // If no module was sorted, but there are still unsorted modules
335 // then there is cyclic dependency.
336 //
337 if ((!moduleWasSorted) && (!moduleSet.isEmpty())) {
338 String errorMsg = "";
339 for (IFloodlightModule module : moduleSet) {
340 if (!errorMsg.isEmpty())
341 errorMsg += ", ";
342 errorMsg += module.getClass().getName();
343 }
344 errorMsg = "ERROR! Cyclic service dependency/dependencies among the following modules: " + errorMsg;
345 throw new FloodlightModuleException(errorMsg);
346 }
347 }
348 moduleSet = orderedModuleSet;
Pavlin Radoslavovc35229e2014-02-06 16:19:37 -0800349
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800350 floodlightModuleContext.setModuleSet(moduleSet);
351 parseConfigParameters(prop);
352 initModules(moduleSet);
353 startupModules(moduleSet);
Ray Milkey269ffb92014-04-03 14:43:30 -0700354
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800355 return floodlightModuleContext;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800356 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700357
358 /**
359 * Loads modules (and their dependencies) specified in the list.
360 *
361 * @param configMods The collection of fully qualified module names to load.
362 * @param prop The list of properties that are configuration options.
363 * @return The ModuleContext containing all the loaded modules.
364 * @throws FloodlightModuleException
365 */
366 public IFloodlightModuleContext loadModulesFromList(Collection<String> configMods, Properties prop)
367 throws FloodlightModuleException {
368 return loadModulesFromList(configMods, prop, null);
369 }
370
371 /**
372 * Add a module to the set of modules to load and register its services
373 *
374 * @param moduleMap the module map
375 * @param moduleSet the module set
376 * @param module the module to add
377 */
378 protected void addModule(Map<Class<? extends IFloodlightService>,
379 IFloodlightModule> moduleMap,
380 Collection<IFloodlightModule> moduleSet,
381 IFloodlightModule module) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800382 if (!moduleSet.contains(module)) {
383 Collection<Class<? extends IFloodlightService>> servs =
384 moduleServiceMap.get(module);
385 if (servs != null) {
386 for (Class<? extends IFloodlightService> c : servs)
387 moduleMap.put(c, module);
388 }
389 moduleSet.add(module);
390 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700391 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800392
393 /**
394 * Allocate service implementations and then init all the modules
Ray Milkey269ffb92014-04-03 14:43:30 -0700395 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800396 * @param moduleSet The set of modules to call their init function on
397 * @throws FloodlightModuleException If a module can not properly be loaded
398 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700399 protected void initModules(Collection<IFloodlightModule> moduleSet)
400 throws FloodlightModuleException {
401 for (IFloodlightModule module : moduleSet) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800402 // Get the module's service instance(s)
Ray Milkey269ffb92014-04-03 14:43:30 -0700403 Map<Class<? extends IFloodlightService>,
404 IFloodlightService> simpls = module.getServiceImpls();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800405
406 // add its services to the context
407 if (simpls != null) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700408 for (Entry<Class<? extends IFloodlightService>,
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800409 IFloodlightService> s : simpls.entrySet()) {
410 if (logger.isDebugEnabled()) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700411 logger.debug("Setting " + s.getValue() +
412 " as provider for " +
413 s.getKey().getCanonicalName());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800414 }
415 if (floodlightModuleContext.getServiceImpl(s.getKey()) == null) {
416 floodlightModuleContext.addService(s.getKey(),
Ray Milkey269ffb92014-04-03 14:43:30 -0700417 s.getValue());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800418 } else {
419 throw new FloodlightModuleException("Cannot set "
Ray Milkey269ffb92014-04-03 14:43:30 -0700420 + s.getValue()
421 + " as the provider for "
422 + s.getKey().getCanonicalName()
423 + " because "
424 + floodlightModuleContext.getServiceImpl(s.getKey())
425 + " already provides it");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800426 }
427 }
428 }
429 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700430
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800431 for (IFloodlightModule module : moduleSet) {
432 // init the module
433 if (logger.isDebugEnabled()) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700434 logger.debug("Initializing " +
435 module.getClass().getCanonicalName());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800436 }
437 module.init(floodlightModuleContext);
438 }
439 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700440
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800441 /**
442 * Call each loaded module's startup method
Ray Milkey269ffb92014-04-03 14:43:30 -0700443 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800444 * @param moduleSet the module set to start up
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700445 * @throws FloodlightModuleException
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800446 */
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700447 protected void startupModules(Collection<IFloodlightModule> moduleSet)
448 throws FloodlightModuleException {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800449 for (IFloodlightModule m : moduleSet) {
450 if (logger.isDebugEnabled()) {
451 logger.debug("Starting " + m.getClass().getCanonicalName());
452 }
453 m.startUp(floodlightModuleContext);
454 }
455 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700456
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800457 /**
458 * Parses configuration parameters for each module
Ray Milkey269ffb92014-04-03 14:43:30 -0700459 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800460 * @param prop The properties file to use
461 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700462 @LogMessageDoc(level = "WARN",
463 message = "Module {module} not found or loaded. " +
464 "Not adding configuration option {key} = {value}",
465 explanation = "Ignoring a configuration parameter for a " +
466 "module that is not loaded.")
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800467 protected void parseConfigParameters(Properties prop) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700468 if (prop == null) return;
469
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800470 Enumeration<?> e = prop.propertyNames();
471 while (e.hasMoreElements()) {
472 String key = (String) e.nextElement();
473 // Ignore module list key
474 if (key.equals(FLOODLIGHT_MODULES_KEY)) {
475 continue;
476 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700477
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800478 String configValue = null;
479 int lastPeriod = key.lastIndexOf(".");
480 String moduleName = key.substring(0, lastPeriod);
481 String configKey = key.substring(lastPeriod + 1);
482 // Check to see if it's overridden on the command line
483 String systemKey = System.getProperty(key);
484 if (systemKey != null) {
485 configValue = systemKey;
486 } else {
487 configValue = prop.getProperty(key);
488 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700489
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800490 IFloodlightModule mod = moduleNameMap.get(moduleName);
491 if (mod == null) {
492 logger.warn("Module {} not found or loaded. " +
Ray Milkey269ffb92014-04-03 14:43:30 -0700493 "Not adding configuration option {} = {}",
494 new Object[]{moduleName, configKey, configValue});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800495 } else {
496 floodlightModuleContext.addConfigParam(mod, configKey, configValue);
497 }
498 }
499 }
500}