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    }