001/* 002 * Copyright (c) 2003-2010 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: michalo $' 006 * '$Date: 2017-08-22 15:52:22 +0000 (Tue, 22 Aug 2017) $' 007 * '$Revision: 34612 $' 008 * 009 * Permission is hereby granted, without written agreement and without 010 * license or royalty fees, to use, copy, modify, and distribute this 011 * software and its documentation for any purpose, provided that the above 012 * copyright notice and the following two paragraphs appear in all copies 013 * of this software. 014 * 015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 019 * SUCH DAMAGE. 020 * 021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 026 * ENHANCEMENTS, OR MODIFICATIONS. 027 * 028 */ 029 030package org.kepler.configuration; 031 032import java.io.File; 033import java.util.ArrayList; 034import java.util.Iterator; 035import java.util.List; 036import java.util.Vector; 037 038import org.kepler.build.modules.Module; 039import org.kepler.build.modules.ModuleTree; 040import org.kepler.build.modules.ModulesTxt; 041import org.kepler.util.DotKeplerManager; 042import org.kepler.util.StatusNotifier; 043 044import ptolemy.util.MessageHandler; 045 046/** 047 * A class to manage configuration options in Kepler. For more information see 048 * https://kepler-project.org/developers/teams/framework/kepler-configuration/ 049 * proposed-future-kepler-configuration-system 050 * and 051 * https://kepler-project.org/developers/teams/framework/configuration-system- 052 * documentation 053 * 054 * @author Chad Berkley 055 * @created October 2009 056 */ 057public class ConfigurationManager 058{ 059 //classes to read and write the configuration 060 protected ConfigurationWriter configWriter; 061 protected ConfigurationReader configReader; 062 063 public static final String dotKeplerConfigurationsDir = 064 (System.getenv("KEPLER_DOT") != null ? System.getenv("KEPLER_DOT") : System.getProperty("user.home")) 065 + File.separator + ".kepler" + File.separator + "configurations"; 066 067 private static ConfigurationManager configurationManager; 068 private List<ConfigurationProperty> propertyList; 069 private Vector<ConfigurationEventListener> listeners; 070 071 /** 072 * private constructor 073 */ 074 private ConfigurationManager() 075 { 076 propertyList = new ArrayList<ConfigurationProperty>(); 077 listeners = new Vector<ConfigurationEventListener>(); 078 //this if block will go away. it should be replaced by a reflections 079 //method to load the correct configurationreader/writer class. 080 //its here for testing only. 081 //use the general configuration writer which writes dirty properties 082 //to the .kepler directory instead of back to the configuration directory 083 configWriter = new GeneralConfigurationWriter(); 084 configReader = new CommonsConfigurationReader(configWriter); 085 086 addConfigurationListener(new SaveListener()); 087 088 } 089 090 /** 091 * singleton accessor 092 */ 093 public static ConfigurationManager getInstance() 094 { 095 return getInstance(true); 096 } 097 098 /** 099 * singleton accessor. set loadConfiguration to true if you want to 100 * load the configuration automatically. This singleton accessor should really 101 * only be used for testing. 102 */ 103 protected static ConfigurationManager getInstance(boolean loadConfiguration) 104 { 105 if (configurationManager == null) 106 { 107 StatusNotifier.log("Initializing Configuration Manager."); 108 configurationManager = new ConfigurationManager(); 109 if(loadConfiguration && ModulesTxt.buildAreaExists()) { 110 try { 111 configurationManager.loadConfiguration(); 112 } catch (ConfigurationManagerException e) { 113 MessageHandler.error("Error loading configurations", e); 114 } 115 } 116 } 117 return configurationManager; 118 } 119 120 /** 121 * add a property to the manager 122 * 123 * @param property the property to add 124 */ 125 public void addProperty(RootConfigurationProperty property) 126 throws NamespaceException 127 { 128 ConfigurationNamespace namespace = property.getNamespace(); 129 List propList = getProperties(property.getModule()); 130 for (int i = 0; i < propList.size(); i++) 131 { 132 RootConfigurationProperty rcp = (RootConfigurationProperty) propList 133 .get(i); 134 if (rcp.getNamespace().equals(namespace)) 135 { 136 throw new NamespaceException("Can't add a second namespace '" 137 + namespace + "' to the configuration for module '" 138 + property.getModule().getName() + "'"); 139 } 140 } 141 propertyList.add(property); 142 } 143 144 /** 145 * set a list of properties all at once 146 * 147 * @param propertyList the list of properties to add 148 */ 149 public void addProperties(List<RootConfigurationProperty> propertyList) 150 throws NamespaceException 151 { 152 for (int i = 0; i < propertyList.size(); i++) 153 { 154 RootConfigurationProperty rcp = propertyList.get(i); 155 addProperty(rcp); 156 } 157 } 158 159 /** 160 * return all properties handled by the manager 161 */ 162 public List<ConfigurationProperty> getProperties() 163 { 164 return propertyList; 165 } 166 167 /** 168 * get a list of properties that belong to a module 169 * 170 * @param module the module to get the property list for 171 */ 172 public List<ConfigurationProperty> getProperties(Module module) 173 { 174 //System.out.println("getting properties for module " + module.getName()); 175 Vector<ConfigurationProperty> v = new Vector<ConfigurationProperty>(); 176 for (int i = 0; i < propertyList.size(); i++) 177 { 178 ConfigurationProperty prop = propertyList.get(i); 179 if (prop.getModule().getName().equals(module.getName())) 180 { 181 v.add(prop); 182 } 183 } 184 return v; 185 } 186 187 /** 188 * get a list of properties that belong to a certain module within a specific 189 * namespace 190 * 191 * @param module 192 * @param namespace namespace within the module to get a property list for 193 */ 194 public List<ConfigurationProperty> getProperties(Module module, 195 ConfigurationNamespace namespace) 196 { 197 Vector<ConfigurationProperty> v = new Vector<ConfigurationProperty>(); 198 List<ConfigurationProperty> l = getProperties(module); 199 for (int i = 0; i < l.size(); i++) 200 { 201 ConfigurationProperty cp = l.get(i); 202 if (cp.getNamespace().equals(namespace)) 203 { 204 v.add(cp); 205 } 206 } 207 return v; 208 } 209 210 /** 211 * get a list of properties that belong to a certain module with a specific 212 * name within a namespace 213 * 214 * @param module 215 * @param namespace namespace within the module 216 * @param name name of the property to get 217 */ 218 public List<ConfigurationProperty> getProperties(Module module, 219 ConfigurationNamespace namespace, String name) 220 { 221 Vector<ConfigurationProperty> v = new Vector<ConfigurationProperty>(); 222 List<ConfigurationProperty> l = getProperties(module, namespace); 223 for (int i = 0; i < l.size(); i++) 224 { 225 ConfigurationProperty cp = l.get(i); 226 List<ConfigurationProperty> propList = cp.getProperties(name); 227 for (int j = 0; j < propList.size(); j++) 228 { 229 v.add(propList.get(j)); 230 } 231 } 232 return v; 233 } 234 235 /** 236 * get a list of properties that belong to a certain module with a specific 237 * name. 238 * this assumes the default namespace. 239 * 240 * @param module 241 * @param name the name of the property to get 242 */ 243 public List<ConfigurationProperty> getProperties(Module module, String name) 244 { 245 return getProperties(module, ConfigurationProperty.namespaceDefault, name); 246 } 247 248 /** 249 * Returns the root property of the default configuration for a module. 250 * This should be stored in 251 * a file named "configuration.xml" in the 252 * <module>/resources/configurations 253 * directory. If no such file exists, this will return null. 254 * 255 * @param module the module to get the default configuration for. 256 */ 257 public ConfigurationProperty getProperty(Module module) 258 { 259 return getProperty(module, ConfigurationProperty.namespaceDefault); 260 } 261 262 /** 263 * return the root ConfigurationProperty of the namespace. 264 * 265 * @param module the module to get the property from 266 * @param namespace the namespace to get the property from 267 */ 268 public ConfigurationProperty getProperty(Module module, 269 ConfigurationNamespace namespace) 270 { 271 List l = getProperties(module); 272 for (int i = 0; i < l.size(); i++) 273 { 274 RootConfigurationProperty rcp = (RootConfigurationProperty) l.get(i); 275 276 if (rcp.getNamespace().equals(namespace)) 277 { 278 return rcp.getRootProperty(); 279 } 280 } 281 return null; 282 } 283 284 /** 285 * get a property from the module's default namespace configuration with 286 * a given name 287 * 288 * @param module the module to get the property from 289 * @param name the name of the property to get. 290 */ 291 public ConfigurationProperty getProperty(Module module, String name) 292 { 293 return getProperty(module, ConfigurationProperty.namespaceDefault, name); 294 } 295 296 /** 297 * return a single property from a module with a specific name in a specific 298 * namespace. If more than one property shares a name, the first is returned. 299 * Return null if the property is not found. 300 * 301 * @param module the module to get the property from 302 * @param namespace the namespace to get the property from 303 * @param name the name of the property to get 304 */ 305 public ConfigurationProperty getProperty(Module module, 306 ConfigurationNamespace namespace, String name) 307 { 308 ConfigurationProperty cp = getProperty(module, namespace); 309 if (cp == null){ 310 return null; 311 } 312 ConfigurationProperty prop = cp.getProperty(name); 313 return prop; 314 } 315 316 /** 317 * serialize the entire configuration 318 */ 319 public void saveConfiguration() throws ConfigurationManagerException 320 { 321 for (int i = 0; i < propertyList.size(); i++) 322 { 323 RootConfigurationProperty prop = (RootConfigurationProperty) propertyList 324 .get(i); 325 configWriter.writeConfiguration(prop); 326 } 327 } 328 329 /** 330 * add a configuration listener 331 * 332 * @param listener the listener to add 333 */ 334 public void addConfigurationListener(ConfigurationEventListener listener) 335 { 336 listeners.add(listener); 337 } 338 339 /** 340 * remove a listener 341 * 342 * @param listener the listener to remove 343 */ 344 public void removeConfigurationListener(ConfigurationEventListener listener) 345 { 346 for (int i = 0; i < listeners.size(); i++) 347 { 348 ConfigurationEventListener cel = listeners.get(i); 349 if (cel == listener) 350 { 351 listeners.remove(i); 352 } 353 } 354 } 355 356 /** 357 * returns the file from the .kepler directory if it exists, returns f if 358 * it doesn't. This method should be used to get configuration files 359 * because if the file has changed it will be written back to .kepler, not 360 * back to the resources/configurations directory of the module. 361 * 362 * @param m the module to get the overwrite file from 363 * @param f the file to get 364 */ 365 public static File getOverwriteFile(Module m, File f) 366 { 367 File dotKeplerConfDir = DotKeplerManager.getInstance() 368 .getModuleConfigurationDirectory(m.getName()); 369 /// .getModuleConfigurationDirectory(m.getStemName()); 370 if (!dotKeplerConfDir.exists()) 371 { 372 return f; 373 } 374 375 String[] files = dotKeplerConfDir.list(); 376 for (int j = 0; j < files.length; j++) 377 { 378 File confFile = new File(dotKeplerConfDir, files[j]); 379 if (confFile.getName().equals(f.getName())) 380 { 381 //found it. return it. 382 return confFile; 383 } 384 } 385 return f; 386 } 387 388 /** 389 * get a module by name. return null if not found 390 * 391 * @param name the name of the module to get 392 */ 393 public static Module getModule(String name) 394 { 395 Module m = ModuleTree.instance().getModuleByStemName(name); 396 return m; 397 } 398 399 /** 400 * notify any listeners that a configuration event has occured 401 * 402 * @param property the property that changed to send to the listeners 403 */ 404 protected void notifyListeners(ConfigurationProperty property) 405 { 406 for (int i = 0; i < listeners.size(); i++) 407 { 408 ConfigurationEventListener listener = listeners.get(i); 409 listener.eventPerformed(new ConfigurationEvent(property)); 410 } 411 } 412 413 /** 414 * deserialize the configuration 415 */ 416 protected void loadConfiguration() throws ConfigurationManagerException 417 { 418 ModuleTree tree = ModuleTree.instance(); 419 Iterator it = tree.iterator(); 420 while (it.hasNext()) 421 { 422 Module m = (Module) it.next(); 423 //System.out.println("loading files from module " + m.getName()); 424 List<RootConfigurationProperty> v = configReader.loadConfigurations(m); 425 426 if (v == null) 427 { 428 continue; 429 } 430 for (int i = 0; i < v.size(); i++) 431 { 432 ConfigurationProperty prop = v.get(i); 433 prop.resetDirty(true); 434 //System.out.println("adding prop to prop list: " + prop.toString(false)); 435 propertyList.add(prop); 436 } 437 } 438 //check to see if any properties of a module have the same namespace and 439 //if so, merge them into one property 440 resolveNamespaces(); 441 } 442 443 /** 444 * remove all configurations currently listed and reset the configuration 445 * manager back to its un-initialized state. This should really only be used 446 * for testing. 447 */ 448 protected void clearConfigurations() 449 { 450 configurationManager = null; 451 propertyList = null; 452 listeners = null; 453 } 454 455 /** 456 * utility method to remove the file extension from the filename 457 * 458 * @param filename the filename to remove the extension from 459 */ 460 protected static String removeFileExtension(String filename) 461 { 462 String s = filename.substring(0, filename.lastIndexOf(".")); 463 return s; 464 } 465 466 /** 467 * remove the locale designator from a filename. this works with 468 * or without a file extension (i.e. .xml). This method assumes 469 * a locale with both a country and language code (i.e. _en_US). 470 */ 471 protected static String removeLocaleDesignator(String filename) 472 { 473 String fn = filename; 474 int i = filename.lastIndexOf("_"); 475 if (i != -1) 476 { 477 filename = filename.substring(0, i); 478 i = filename.lastIndexOf("_"); 479 if (i != -1) 480 { 481 filename = filename.substring(0, i); 482 return filename; 483 } 484 } 485 486 return fn; 487 } 488 489 /** 490 * check to see if any properties of a module have the same namespace and 491 * if so, merge them into one property 492 */ 493 private void resolveNamespaces() 494 { 495 //ConfigurationProperty.simplePrintList(getProperties()); 496 ModuleTree tree = ModuleTree.instance(); 497 Iterator it = tree.iterator(); 498 while (it.hasNext()) 499 { 500 boolean done = false; 501 Module m = (Module) it.next(); 502 List props = getProperties(m); 503 for (int i = 0; i < props.size(); i++) 504 { //go through the properties and check to see if any have the same namespace 505 ConfigurationProperty cp = (ConfigurationProperty) props.get(i); 506 for (int j = 0; j < props.size(); j++) 507 { 508 ConfigurationProperty cp2 = (ConfigurationProperty) props.get(j); 509 if (i != j) 510 { 511 if (cp.getNamespace().equals(cp2.getNamespace())) 512 { //same namespace is found. merge them and get out of the loops 513 RootConfigurationProperty rcp = new RootConfigurationProperty(m, 514 cp.getNamespace().toString(), cp.getNamespace()); 515 try 516 { 517 rcp.addProperty(((RootConfigurationProperty) cp) 518 .getRootProperty()); 519 rcp.addProperty(((RootConfigurationProperty) cp2) 520 .getRootProperty()); 521 propertyList.remove(cp); 522 propertyList.remove(cp2); 523 propertyList.add(rcp); 524 done = true; 525 break; 526 } 527 catch (Exception e) 528 { 529 System.out.println("Error merging namesapace properties. " 530 + "This exception should not be happening: " 531 + e.getMessage()); 532 } 533 } 534 } 535 } 536 537 if (done) 538 { 539 break; 540 } 541 } 542 } 543 } 544}