001 /** 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one or more 004 * contributor license agreements. See the NOTICE file distributed with 005 * this work for additional information regarding copyright ownership. 006 * The ASF licenses this file to You under the Apache License, Version 2.0 007 * (the "License"); you may not use this file except in compliance with 008 * the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 package org.apache.geronimo.system.configuration; 019 020 import org.apache.commons.logging.Log; 021 import org.apache.commons.logging.LogFactory; 022 import org.apache.geronimo.common.propertyeditor.PropertyEditors; 023 import org.apache.geronimo.gbean.AbstractName; 024 import org.apache.geronimo.gbean.GAttributeInfo; 025 import org.apache.geronimo.gbean.GBeanData; 026 import org.apache.geronimo.gbean.GBeanInfo; 027 import org.apache.geronimo.gbean.GBeanInfoBuilder; 028 import org.apache.geronimo.gbean.GBeanLifecycle; 029 import org.apache.geronimo.gbean.GReferenceInfo; 030 import org.apache.geronimo.gbean.ReferencePatterns; 031 import org.apache.geronimo.kernel.config.InvalidConfigException; 032 import org.apache.geronimo.kernel.config.ManageableAttributeStore; 033 import org.apache.geronimo.kernel.config.PersistentConfigurationList; 034 import org.apache.geronimo.kernel.config.Configuration; 035 import org.apache.geronimo.kernel.repository.Artifact; 036 import org.apache.geronimo.kernel.InvalidGBeanException; 037 import org.apache.geronimo.kernel.util.XmlUtil; 038 import org.apache.geronimo.system.serverinfo.ServerInfo; 039 import org.w3c.dom.Document; 040 import org.w3c.dom.Element; 041 import org.xml.sax.ErrorHandler; 042 import org.xml.sax.InputSource; 043 import org.xml.sax.SAXException; 044 import org.xml.sax.SAXParseException; 045 046 import javax.xml.parsers.DocumentBuilder; 047 import javax.xml.parsers.DocumentBuilderFactory; 048 import javax.xml.parsers.ParserConfigurationException; 049 import javax.xml.transform.TransformerFactory; 050 import javax.xml.transform.Transformer; 051 import javax.xml.transform.OutputKeys; 052 import javax.xml.transform.TransformerException; 053 import javax.xml.transform.stream.StreamResult; 054 import javax.xml.transform.dom.DOMSource; 055 056 import java.beans.PropertyEditor; 057 058 import java.io.File; 059 import java.io.FileInputStream; 060 import java.io.InputStream; 061 import java.io.BufferedInputStream; 062 import java.io.FileNotFoundException; 063 import java.io.FileOutputStream; 064 import java.io.IOException; 065 import java.io.OutputStream; 066 import java.io.BufferedOutputStream; 067 068 import java.util.ArrayList; 069 import java.util.Collection; 070 import java.util.HashMap; 071 import java.util.Iterator; 072 import java.util.List; 073 import java.util.Map; 074 import java.util.Timer; 075 import java.util.TimerTask; 076 077 /** 078 * Stores managed attributes in an XML file on the local filesystem. 079 * 080 * @version $Rev: 470597 $ $Date: 2006-11-02 15:30:55 -0800 (Thu, 02 Nov 2006) $ 081 */ 082 public class LocalAttributeManager implements PluginAttributeStore, PersistentConfigurationList, GBeanLifecycle { 083 private static final Log log = LogFactory.getLog(LocalAttributeManager.class); 084 085 private static final String CONFIG_FILE_PROPERTY = "org.apache.geronimo.config.file"; 086 087 private static final String BACKUP_EXTENSION = ".bak"; 088 private static final String TEMP_EXTENSION = ".working"; 089 private static final int SAVE_BUFFER_MS = 5000; 090 091 private final ServerInfo serverInfo; 092 private final String configFile; 093 private final boolean readOnly; 094 095 private File attributeFile; 096 private File backupFile; 097 private File tempFile; 098 private ServerOverride serverOverride; 099 100 private Timer timer; 101 private TimerTask currentTask; 102 103 private boolean kernelFullyStarted; 104 105 public LocalAttributeManager(String configFile, boolean readOnly, ServerInfo serverInfo) { 106 this.configFile = System.getProperty(CONFIG_FILE_PROPERTY, configFile); 107 this.readOnly = readOnly; 108 this.serverInfo = serverInfo; 109 serverOverride = new ServerOverride(); 110 } 111 112 public boolean isReadOnly() { 113 return readOnly; 114 } 115 116 public synchronized Collection applyOverrides(Artifact configName, Collection gbeanDatas, ClassLoader classLoader) throws InvalidConfigException { 117 // clone the datas since we will be modifying this collection 118 gbeanDatas = new ArrayList(gbeanDatas); 119 120 ConfigurationOverride configuration = serverOverride.getConfiguration(configName); 121 if (configuration == null) { 122 return gbeanDatas; 123 } 124 125 // index the incoming datas 126 Map datasByName = new HashMap(); 127 for (Iterator iterator = gbeanDatas.iterator(); iterator.hasNext();) { 128 GBeanData gbeanData = (GBeanData) iterator.next(); 129 datasByName.put(gbeanData.getAbstractName(), gbeanData); 130 datasByName.put(gbeanData.getAbstractName().getName().get("name"), gbeanData); 131 } 132 133 // add the new GBeans 134 for (Iterator iterator = configuration.getGBeans().entrySet().iterator(); iterator.hasNext();) { 135 Map.Entry entry = (Map.Entry) iterator.next(); 136 Object name = entry.getKey(); 137 GBeanOverride gbean = (GBeanOverride) entry.getValue(); 138 if (!datasByName.containsKey(name) && gbean.isLoad()) { 139 if (gbean.getGBeanInfo() == null || !(name instanceof AbstractName)) { 140 String sep = ""; 141 StringBuffer message = new StringBuffer("New GBeans must be specified with "); 142 if (gbean.getGBeanInfo() == null) { 143 message.append("a GBeanInfo "); 144 sep = "and "; 145 } 146 if (!(name instanceof AbstractName)) { 147 message.append(sep).append("a full AbstractName "); 148 } 149 message.append("configuration=").append(configName); 150 message.append(" gbeanName=").append(name); 151 throw new InvalidConfigException(message.toString()); 152 } 153 GBeanInfo gbeanInfo = GBeanInfo.getGBeanInfo(gbean.getGBeanInfo(), classLoader); 154 AbstractName abstractName = (AbstractName)name; 155 GBeanData gBeanData = new GBeanData(abstractName, gbeanInfo); 156 gbeanDatas.add(gBeanData); 157 } 158 } 159 160 // set the attributes 161 for (Iterator iterator = gbeanDatas.iterator(); iterator.hasNext();) { 162 GBeanData data = (GBeanData) iterator.next(); 163 boolean load = setAttributes(data, configuration, configName, classLoader); 164 if (!load) { 165 iterator.remove(); 166 } 167 } 168 return gbeanDatas; 169 } 170 171 /** 172 * Set the attributes from the attribute store on a single gbean, and return whether or not to load the gbean. 173 * 174 * @param data 175 * @param configuration 176 * @param configName 177 * @param classLoader 178 * @return true if the gbean should be loaded, false otherwise. 179 * @throws org.apache.geronimo.kernel.config.InvalidConfigException 180 * 181 */ 182 private synchronized boolean setAttributes(GBeanData data, ConfigurationOverride configuration, Artifact configName, ClassLoader classLoader) throws InvalidConfigException { 183 AbstractName gbeanName = data.getAbstractName(); 184 GBeanOverride gbean = configuration.getGBean(gbeanName); 185 if (gbean == null) { 186 gbean = configuration.getGBean((String) gbeanName.getName().get("name")); 187 } 188 189 if (gbean == null) { 190 //no attr info, load by default 191 return true; 192 } 193 194 if (!gbean.isLoad()) { 195 return false; 196 } 197 198 GBeanInfo gbeanInfo = data.getGBeanInfo(); 199 200 // set attributes 201 for (Iterator iterator = gbean.getAttributes().entrySet().iterator(); iterator.hasNext();) { 202 Map.Entry entry = (Map.Entry) iterator.next(); 203 String attributeName = (String) entry.getKey(); 204 GAttributeInfo attributeInfo = gbeanInfo.getAttribute(attributeName); 205 if (attributeInfo == null) { 206 throw new InvalidConfigException("No attribute: " + attributeName + " for gbean: " + data.getAbstractName()); 207 } 208 String valueString = (String) entry.getValue(); 209 Object value = getValue(attributeInfo, valueString, configName, gbeanName, classLoader); 210 data.setAttribute(attributeName, value); 211 } 212 213 //Clear attributes 214 for (Iterator iterator = gbean.getClearAttributes().iterator(); iterator.hasNext();){ 215 String attribute = (String) iterator.next(); 216 if (gbean.getClearAttribute(attribute)){ 217 data.clearAttribute(attribute); 218 } 219 } 220 221 //Null attributes 222 for (Iterator iterator = gbean.getNullAttributes().iterator(); iterator.hasNext();){ 223 String attribute = (String) iterator.next(); 224 if (gbean.getNullAttribute(attribute)){ 225 data.setAttribute(attribute, null); 226 } 227 } 228 229 // set references 230 for (Iterator iterator = gbean.getReferences().entrySet().iterator(); iterator.hasNext();) { 231 Map.Entry entry = (Map.Entry) iterator.next(); 232 233 String referenceName = (String) entry.getKey(); 234 GReferenceInfo referenceInfo = gbeanInfo.getReference(referenceName); 235 if (referenceInfo == null) { 236 throw new InvalidConfigException("No reference: " + referenceName + " for gbean: " + data.getAbstractName()); 237 } 238 239 ReferencePatterns referencePatterns = (ReferencePatterns) entry.getValue(); 240 241 data.setReferencePatterns(referenceName, referencePatterns); 242 } 243 244 //Clear references 245 for (Iterator iterator = gbean.getClearReferences().iterator(); iterator.hasNext();){ 246 String reference = (String) iterator.next(); 247 if (gbean.getClearReference(reference)){ 248 data.clearReference(reference); 249 } 250 } 251 252 return true; 253 } 254 255 256 private synchronized Object getValue(GAttributeInfo attribute, String value, Artifact configurationName, AbstractName gbeanName, ClassLoader classLoader) { 257 if (value == null) { 258 return null; 259 } 260 261 try { 262 PropertyEditor editor = PropertyEditors.findEditor(attribute.getType(), classLoader); 263 if (editor == null) { 264 log.debug("Unable to parse attribute of type " + attribute.getType() + "; no editor found"); 265 return null; 266 } 267 editor.setAsText(value); 268 log.debug("Setting value for " + configurationName + "/" + gbeanName + "/" + attribute.getName() + " to value " + value); 269 return editor.getValue(); 270 } catch (ClassNotFoundException e) { 271 log.error("Unable to load attribute type " + attribute.getType()); 272 return null; 273 } 274 } 275 276 public void setModuleGBeans(Artifact moduleName, GBeanOverride[] gbeans) { 277 if (readOnly) { 278 return; 279 } 280 ConfigurationOverride configuration = serverOverride.getConfiguration(moduleName, true); 281 for (int i = 0; i < gbeans.length; i++) { 282 GBeanOverride gbean = gbeans[i]; 283 configuration.addGBean(gbean); 284 } 285 attributeChanged(); 286 } 287 288 public synchronized void setValue(Artifact configurationName, AbstractName gbeanName, GAttributeInfo attribute, Object value) { 289 if (readOnly) { 290 return; 291 } 292 ConfigurationOverride configuration = serverOverride.getConfiguration(configurationName, true); 293 GBeanOverride gbean = configuration.getGBean(gbeanName); 294 if (gbean == null) { 295 gbean = configuration.getGBean((String) gbeanName.getName().get("name")); 296 if (gbean == null) { 297 gbean = new GBeanOverride(gbeanName, true); 298 configuration.addGBean(gbeanName, gbean); 299 } 300 } 301 302 try { 303 gbean.setAttribute(attribute.getName(), value, attribute.getType()); 304 attributeChanged(); 305 } catch (InvalidAttributeException e) { 306 // attribute can not be represented as a string 307 log.error(e.getMessage()); 308 } 309 } 310 311 public synchronized void setReferencePatterns(Artifact configurationName, AbstractName gbeanName, GReferenceInfo reference, ReferencePatterns patterns) { 312 if (readOnly) { 313 return; 314 } 315 316 ConfigurationOverride configuration = serverOverride.getConfiguration(configurationName, true); 317 GBeanOverride gbean = configuration.getGBean(gbeanName); 318 if (gbean == null) { 319 gbean = configuration.getGBean((String)gbeanName.getName().get("name")); 320 if (gbean == null) { 321 gbean = new GBeanOverride(gbeanName, true); 322 configuration.addGBean(gbeanName, gbean); 323 } 324 } 325 gbean.setReferencePatterns(reference.getName(), patterns); 326 attributeChanged(); 327 } 328 329 public synchronized void setShouldLoad(Artifact configurationName, AbstractName gbeanName, boolean load) { 330 if (readOnly) { 331 return; 332 } 333 ConfigurationOverride configuration = serverOverride.getConfiguration(configurationName, true); 334 335 GBeanOverride gbean = configuration.getGBean(gbeanName); 336 if (gbean == null) { 337 // attempt to lookup by short name 338 gbean = configuration.getGBean((String)gbeanName.getName().get("name")); 339 } 340 341 if (gbean == null) { 342 gbean = new GBeanOverride(gbeanName, load); 343 configuration.addGBean(gbeanName, gbean); 344 } else { 345 gbean.setLoad(load); 346 } 347 attributeChanged(); 348 } 349 350 public void addGBean(Artifact configurationName, GBeanData gbeanData) { 351 if (readOnly) { 352 return; 353 } 354 ConfigurationOverride configuration = serverOverride.getConfiguration(configurationName); 355 if (configuration == null) { 356 log.debug("Can not add GBean; Configuration not found " + configurationName); 357 return; 358 } 359 try { 360 GBeanOverride gbean = new GBeanOverride(gbeanData); 361 configuration.addGBean(gbean); 362 attributeChanged(); 363 } catch (InvalidAttributeException e) { 364 // attribute can not be represented as a string 365 log.error(e.getMessage()); 366 } 367 } 368 369 public synchronized void load() throws IOException { 370 ensureParentDirectory(); 371 if (!attributeFile.exists()) { 372 return; 373 } 374 375 InputStream input = new BufferedInputStream(new FileInputStream(attributeFile)); 376 InputSource source = new InputSource(input); 377 source.setSystemId(attributeFile.toString()); 378 DocumentBuilderFactory dFactory = XmlUtil.newDocumentBuilderFactory(); 379 380 try { 381 dFactory.setValidating(true); 382 dFactory.setNamespaceAware(true); 383 dFactory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage", 384 "http://www.w3.org/2001/XMLSchema"); 385 386 dFactory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaSource", 387 LocalAttributeManager.class.getResourceAsStream("/META-INF/schema/attributes-1.1.xsd")); 388 389 DocumentBuilder builder = dFactory.newDocumentBuilder(); 390 builder.setErrorHandler(new ErrorHandler() { 391 public void error(SAXParseException e) { 392 log.error("Unable to read saved manageable attributes. " + 393 "SAX parse error: " + e.getMessage() + 394 " at line " + e.getLineNumber() + 395 ", column " + e.getColumnNumber() + 396 " in entity " + e.getSystemId()); 397 398 if (log.isTraceEnabled()) { 399 log.trace("Exception deatils", e); 400 } 401 402 // TODO throw an exception here? 403 } 404 405 public void fatalError(SAXParseException e) { 406 log.error("Unable to read saved manageable attributes. " + 407 "Fatal SAX parse error: " + e.getMessage() + 408 " at line " + e.getLineNumber() + 409 ", column " + e.getColumnNumber() + 410 " in entity " + e.getSystemId()); 411 412 if (log.isTraceEnabled()) { 413 log.trace("Exception deatils", e); 414 } 415 416 // TODO throw an exception here? 417 } 418 419 public void warning(SAXParseException e) { 420 log.error("SAX parse warning whilst reading saved manageable attributes: " + 421 e.getMessage() + 422 " at line " + e.getLineNumber() + 423 ", column " + e.getColumnNumber() + 424 " in entity " + e.getSystemId()); 425 426 if (log.isTraceEnabled()) { 427 log.trace("Exception deatils", e); 428 } 429 } 430 }); 431 432 Document doc = builder.parse(source); 433 Element root = doc.getDocumentElement(); 434 serverOverride = new ServerOverride(root); 435 } catch (SAXException e) { 436 log.error("Unable to read saved manageable attributes", e); 437 } catch (ParserConfigurationException e) { 438 log.error("Unable to read saved manageable attributes", e); 439 } catch (InvalidGBeanException e) { 440 log.error("Unable to read saved manageable attributes", e); 441 } finally { 442 // input is always non-null 443 input.close(); 444 } 445 } 446 447 public synchronized void save() throws IOException { 448 if (readOnly) { 449 return; 450 } 451 ensureParentDirectory(); 452 if (!tempFile.exists() && !tempFile.createNewFile()) { 453 throw new IOException("Unable to create manageable attribute working file for save " + tempFile.getAbsolutePath()); 454 } 455 if (!tempFile.canWrite()) { 456 throw new IOException("Unable to write to manageable attribute working file for save " + tempFile.getAbsolutePath()); 457 } 458 459 // write the new configuration to the temp file 460 saveXmlToFile(tempFile, serverOverride); 461 462 // delete the current backup file 463 if (backupFile.exists()) { 464 if (!backupFile.delete()) { 465 throw new IOException("Unable to delete old backup file in order to back up current manageable attribute working file for save"); 466 } 467 } 468 469 // rename the existing configuration file to the backup file 470 if (attributeFile.exists()) { 471 if (!attributeFile.renameTo(backupFile)) { 472 throw new IOException("Unable to rename " + attributeFile.getAbsolutePath() + " to " + backupFile.getAbsolutePath() + " in order to back up manageable attribute save file"); 473 } 474 } 475 476 // rename the temp file the the configuration file 477 if (!tempFile.renameTo(attributeFile)) { 478 throw new IOException("EXTREMELY CRITICAL! Unable to move manageable attributes working file to proper file name! Configuration will revert to defaults unless this is manually corrected! (could not rename " + tempFile.getAbsolutePath() + " to " + attributeFile.getAbsolutePath() + ")"); 479 } 480 } 481 482 private static void saveXmlToFile(File file, ServerOverride serverOverride) { 483 DocumentBuilderFactory dFactory = XmlUtil.newDocumentBuilderFactory(); 484 dFactory.setValidating(true); 485 dFactory.setNamespaceAware(true); 486 dFactory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage", 487 "http://www.w3.org/2001/XMLSchema"); 488 dFactory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaSource", 489 LocalAttributeManager.class.getResourceAsStream("/META-INF/schema/attributes-1.1.xsd")); 490 491 OutputStream output = null; 492 try { 493 Document doc = dFactory.newDocumentBuilder().newDocument(); 494 serverOverride.writeXml(doc); 495 TransformerFactory xfactory = XmlUtil.newTransformerFactory(); 496 Transformer xform = xfactory.newTransformer(); 497 xform.setOutputProperty(OutputKeys.INDENT, "yes"); 498 xform.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); 499 output = new BufferedOutputStream(new FileOutputStream(file)); 500 501 // use a FileOutputStream instead of a File on the StreamResult 502 // constructor as problems were encountered with the file not being closed. 503 StreamResult sr = new StreamResult(output); 504 xform.transform(new DOMSource(doc), sr); 505 506 output.flush(); 507 } catch (FileNotFoundException e) { 508 // file is directory or cannot be created/opened 509 log.error("Unable to write config.xml", e); 510 } catch (ParserConfigurationException e) { 511 log.error("Unable to write config.xml", e); 512 } catch (TransformerException e) { 513 log.error("Unable to write config.xml", e); 514 } catch (IOException e) { 515 log.error("Unable to write config.xml", e); 516 } finally { 517 if (output != null) { 518 try { 519 output.close(); 520 } catch (IOException ignored) { 521 // ignored 522 } 523 } 524 } 525 } 526 527 //PersistentConfigurationList 528 public synchronized boolean isKernelFullyStarted() { 529 return kernelFullyStarted; 530 } 531 532 public synchronized void setKernelFullyStarted(boolean kernelFullyStarted) { 533 this.kernelFullyStarted = kernelFullyStarted; 534 } 535 536 public synchronized List restore() throws IOException { 537 List configs = new ArrayList(); 538 for (Iterator iterator = serverOverride.getConfigurations().entrySet().iterator(); iterator.hasNext();) { 539 Map.Entry entry = (Map.Entry) iterator.next(); 540 ConfigurationOverride configuration = (ConfigurationOverride) entry.getValue(); 541 if (configuration.isLoad()) { 542 Artifact configID = (Artifact) entry.getKey(); 543 configs.add(configID); 544 } 545 } 546 return configs; 547 } 548 549 public void startConfiguration(Artifact configurationName) { 550 ConfigurationOverride configuration = serverOverride.getConfiguration(configurationName, false); 551 if(configuration == null) { 552 return; 553 } 554 configuration.setLoad(true); 555 attributeChanged(); 556 } 557 558 public synchronized void addConfiguration(Artifact configurationName) { 559 // Check whether we have it already 560 ConfigurationOverride configuration = serverOverride.getConfiguration(configurationName, false); 561 // If not, initialize it 562 if(configuration == null) { 563 configuration = serverOverride.getConfiguration(configurationName, true); 564 configuration.setLoad(false); 565 attributeChanged(); 566 } 567 } 568 569 public synchronized void removeConfiguration(Artifact configName) { 570 ConfigurationOverride configuration = serverOverride.getConfiguration(configName); 571 if (configuration == null) { 572 return; 573 } 574 serverOverride.removeConfiguration(configName); 575 attributeChanged(); 576 } 577 578 public Artifact[] getListedConfigurations(Artifact query) { 579 return serverOverride.queryConfigurations(query); 580 } 581 582 public void stopConfiguration(Artifact configName) { 583 ConfigurationOverride configuration = serverOverride.getConfiguration(configName); 584 if (configuration == null) { 585 return; 586 } 587 configuration.setLoad(false); 588 attributeChanged(); 589 } 590 591 public void migrateConfiguration(Artifact oldName, Artifact newName, Configuration configuration) { 592 ConfigurationOverride configInfo = serverOverride.getConfiguration(oldName); 593 if(configInfo == null) { 594 throw new IllegalArgumentException("Trying to migrate unknown configuration: " + oldName); 595 } 596 serverOverride.removeConfiguration(oldName); 597 configInfo = new ConfigurationOverride(configInfo, newName); 598 //todo: check whether all the attributes are still valid for the new configuration 599 serverOverride.addConfiguration(configInfo); 600 attributeChanged(); 601 } 602 603 //GBeanLifeCycle 604 public synchronized void doStart() throws Exception { 605 load(); 606 if (!readOnly) { 607 timer = new Timer(); 608 } 609 log.debug("Started LocalAttributeManager with data on " + serverOverride.getConfigurations().size() + " configurations"); 610 } 611 612 public synchronized void doStop() throws Exception { 613 boolean doSave = false; 614 synchronized (this) { 615 if (timer != null) { 616 timer.cancel(); 617 if (currentTask != null) { 618 currentTask.cancel(); 619 doSave = true; 620 } 621 } 622 } 623 if (doSave) { 624 save(); 625 } 626 log.debug("Stopped LocalAttributeManager with data on " + serverOverride.getConfigurations().size() + " configurations"); 627 serverOverride = new ServerOverride(); 628 } 629 630 public synchronized void doFail() { 631 synchronized (this) { 632 if (timer != null) { 633 timer.cancel(); 634 if (currentTask != null) { 635 currentTask.cancel(); 636 } 637 } 638 } 639 serverOverride = new ServerOverride(); 640 } 641 642 private synchronized void ensureParentDirectory() throws IOException { 643 if (attributeFile == null) { 644 attributeFile = serverInfo.resolveServer(configFile); 645 tempFile = new File(attributeFile.getAbsolutePath() + TEMP_EXTENSION); 646 backupFile = new File(attributeFile.getAbsolutePath() + BACKUP_EXTENSION); 647 } 648 File parent = attributeFile.getParentFile(); 649 if (!parent.isDirectory()) { 650 if (!parent.mkdirs()) { 651 throw new IOException("Unable to create directory for list:" + parent); 652 } 653 } 654 if (!parent.canRead() || !parent.canWrite()) { 655 throw new IOException("Unable to write manageable attribute files to directory " + parent.getAbsolutePath()); 656 } 657 } 658 659 private synchronized void attributeChanged() { 660 if (currentTask != null) { 661 currentTask.cancel(); 662 } 663 if (timer != null) { 664 currentTask = new TimerTask() { 665 666 public void run() { 667 try { 668 LocalAttributeManager.this.save(); 669 } catch (IOException e) { 670 log.error("Error saving attributes", e); 671 } 672 } 673 }; 674 timer.schedule(currentTask, SAVE_BUFFER_MS); 675 } 676 } 677 678 public static final GBeanInfo GBEAN_INFO; 679 680 static { 681 GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic(LocalAttributeManager.class, "AttributeStore"); 682 infoFactory.addReference("ServerInfo", ServerInfo.class, "GBean"); 683 infoFactory.addAttribute("configFile", String.class, true); 684 infoFactory.addAttribute("readOnly", boolean.class, true); 685 infoFactory.addInterface(ManageableAttributeStore.class); 686 infoFactory.addInterface(PersistentConfigurationList.class); 687 688 infoFactory.setConstructor(new String[]{"configFile", "readOnly", "ServerInfo"}); 689 690 GBEAN_INFO = infoFactory.getBeanInfo(); 691 } 692 693 public static GBeanInfo getGBeanInfo() { 694 return GBEAN_INFO; 695 } 696 }