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