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