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