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 }