1 /**
2 *
3 * Copyright 2003-2004 The Apache Software Foundation
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.geronimo.system.logging.log4j;
19
20 import java.io.File;
21 import java.io.FileInputStream;
22 import java.io.FileOutputStream;
23 import java.io.IOException;
24 import java.io.InputStreamReader;
25 import java.io.OutputStream;
26 import java.io.RandomAccessFile;
27 import java.io.Reader;
28 import java.net.MalformedURLException;
29 import java.nio.CharBuffer;
30 import java.nio.MappedByteBuffer;
31 import java.nio.channels.FileChannel;
32 import java.nio.charset.Charset;
33 import java.util.ArrayList;
34 import java.util.Enumeration;
35 import java.util.Iterator;
36 import java.util.LinkedList;
37 import java.util.List;
38 import java.util.Set;
39 import java.util.Timer;
40 import java.util.TimerTask;
41 import java.util.regex.Matcher;
42 import java.util.regex.Pattern;
43 import java.util.regex.PatternSyntaxException;
44
45 import org.apache.commons.logging.Log;
46 import org.apache.commons.logging.LogConfigurationException;
47 import org.apache.commons.logging.LogFactory;
48 import org.apache.geronimo.gbean.GBeanInfo;
49 import org.apache.geronimo.gbean.GBeanInfoBuilder;
50 import org.apache.geronimo.gbean.GBeanLifecycle;
51 import org.apache.geronimo.kernel.log.GeronimoLogFactory;
52 import org.apache.geronimo.kernel.log.GeronimoLogging;
53 import org.apache.geronimo.system.logging.SystemLog;
54 import org.apache.geronimo.system.serverinfo.DirectoryUtils;
55 import org.apache.geronimo.system.serverinfo.ServerConstants;
56 import org.apache.geronimo.system.serverinfo.ServerInfo;
57 import org.apache.log4j.FileAppender;
58 import org.apache.log4j.Level;
59 import org.apache.log4j.LogManager;
60 import org.apache.log4j.Logger;
61
62 /**
63 * A Log4j logging service.
64 *
65 * @version $Rev: 471832 $ $Date: 2006-11-06 11:09:50 -0800 (Mon, 06 Nov 2006) $
66 */
67 public class Log4jService implements GBeanLifecycle, SystemLog {
68
69 private final static Pattern VARIABLE_PATTERN = Pattern.compile("\\$\\{.*?\\}");
70
71 private final static Pattern DEFAULT_ANY_START = Pattern.compile("^\\d\\d\\:\\d\\d\\:\\d\\d\\,\\d\\d\\d (TRACE|DEBUG|INFO|WARN|ERROR|FATAL) .*");
72 private final static Pattern DEFAULT_FATAL_START = Pattern.compile("^\\d\\d\\:\\d\\d\\:\\d\\d\\,\\d\\d\\d FATAL .*");
73 private final static Pattern DEFAULT_ERROR_START = Pattern.compile("^\\d\\d\\:\\d\\d\\:\\d\\d\\,\\d\\d\\d (ERROR|FATAL) .*");
74 private final static Pattern DEFAULT_WARN_START = Pattern.compile("^\\d\\d\\:\\d\\d\\:\\d\\d\\,\\d\\d\\d (WARN|ERROR|FATAL) .*");
75 private final static Pattern DEFAULT_INFO_START = Pattern.compile("^\\d\\d\\:\\d\\d\\:\\d\\d\\,\\d\\d\\d (INFO|WARN|ERROR|FATAL) .*");
76 private final static Pattern DEFAULT_DEBUG_START = Pattern.compile("^\\d\\d\\:\\d\\d\\:\\d\\d\\,\\d\\d\\d (DEBUG|INFO|WARN|ERROR|FATAL) .*");
77
78 private final static Pattern UNKNOWN_ANY_START = Pattern.compile("(TRACE|DEBUG|INFO|WARN|ERROR|FATAL)");
79 private final static Pattern UNKNOWN_FATAL_START = Pattern.compile("FATAL");
80 private final static Pattern UNKNOWN_ERROR_START = Pattern.compile("(ERROR|FATAL)");
81 private final static Pattern UNKNOWN_WARN_START = Pattern.compile("(WARN|ERROR|FATAL)");
82 private final static Pattern UNKNOWN_INFO_START = Pattern.compile("(INFO|WARN|ERROR|FATAL)");
83 private final static Pattern UNKNOWN_DEBUG_START = Pattern.compile("(DEBUG|INFO|WARN|ERROR|FATAL)");
84
85 private final static Pattern FULL_LINE_PATTERN = Pattern.compile("^.*", Pattern.MULTILINE);
86
87
88 /**
89 * The URL to the configuration file.
90 */
91 private String configurationFile;
92
93 /**
94 * The time (in seconds) between checking for new config.
95 */
96 private int refreshPeriod;
97
98 /**
99 * The properties service
100 */
101 private final ServerInfo serverInfo;
102
103 /**
104 * The URL watch timer (in daemon mode).
105 */
106 private Timer timer = new Timer(true);
107
108 /**
109 * A monitor to check when the config URL changes.
110 */
111 private TimerTask monitor;
112
113 /**
114 * Last time the file was changed.
115 */
116 private long lastChanged = -1;
117
118 /**
119 * Is this service running?
120 */
121 private boolean running = false;
122
123 /**
124 * Construct a <code>Log4jService</code>.
125 *
126 * @param configurationFile The log4j configuration file.
127 * @param refreshPeriod The refresh refreshPeriod (in seconds).
128 */
129 public Log4jService(final String configurationFile, final int refreshPeriod, ServerInfo serverInfo) {
130 this.refreshPeriod = refreshPeriod;
131 this.configurationFile = configurationFile;
132 this.serverInfo = serverInfo;
133 }
134
135 /**
136 * Gets the level of the root logger.
137 */
138 public synchronized String getRootLoggerLevel() {
139 Level level = LogManager.getRootLogger().getLevel();
140
141 if (level != null) {
142 return level.toString();
143 }
144
145 return null;
146 }
147
148 /**
149 * Sets the level of the root logger.
150 *
151 * @param level The level to change the logger to.
152 */
153 public synchronized void setRootLoggerLevel(final String level) {
154
155 String currentLevel = this.getRootLoggerLevel();
156
157
158 if (!currentLevel.equals(level)) {
159 LogManager.getRootLogger().setLevel(XLevel.toLevel(level));
160 }
161 }
162
163 /**
164 * Gets the level of the logger of the give name.
165 *
166 * @param logger The logger to inspect.
167 */
168 public String getLoggerEffectiveLevel(final String logger) {
169 if (logger == null) {
170 throw new IllegalArgumentException("logger is null");
171 }
172
173 Level level = LogManager.getLogger(logger).getEffectiveLevel();
174
175 if (level != null) {
176 return level.toString();
177 }
178
179 return null;
180 }
181
182 /**
183 * Gets the level of the logger of the give name.
184 *
185 * @param logger The logger to inspect.
186 */
187 public String getLoggerLevel(final String logger) {
188 if (logger == null) {
189 throw new IllegalArgumentException("logger is null");
190 }
191
192 Level level = LogManager.getLogger(logger).getLevel();
193
194 if (level != null) {
195 return level.toString();
196 }
197
198 return null;
199 }
200
201 /**
202 * Sets the level for a logger of the give name.
203 *
204 * @param logger The logger to change level
205 * @param level The level to change the logger to.
206 */
207 public void setLoggerLevel(final String logger, final String level) {
208 if (logger == null) {
209 throw new IllegalArgumentException("logger is null");
210 }
211 if (level == null) {
212 throw new IllegalArgumentException("level is null");
213 }
214
215 Logger.getLogger(logger).setLevel(XLevel.toLevel(level));
216 }
217
218 /**
219 * Get the refresh period.
220 *
221 * @return the refresh period (in seconds)
222 */
223 public synchronized int getRefreshPeriodSeconds() {
224 return refreshPeriod;
225 }
226
227 /**
228 * Set the refresh period.
229 *
230 * @param period the refresh period (in seconds)
231 * @throws IllegalArgumentException if refresh period is <= 0
232 */
233 public synchronized void setRefreshPeriodSeconds(final int period) {
234 if (period < 1) {
235 throw new IllegalArgumentException("Refresh period must be > 0");
236 }
237
238 if (this.refreshPeriod != period) {
239 this.refreshPeriod = period;
240 schedule();
241 }
242 }
243
244 /**
245 * Get the logging configuration URL.
246 *
247 * @return the logging configuration URL
248 */
249 public synchronized String getConfigFileName() {
250 return configurationFile;
251 }
252
253 /**
254 * Set the logging configuration URL.
255 *
256 * @param configurationFile the logging configuration file
257 */
258 public synchronized void setConfigFileName(final String configurationFile) {
259 if (configurationFile == null) {
260 throw new IllegalArgumentException("configurationFile is null");
261 }
262
263
264 if (!this.configurationFile.equals(configurationFile)) {
265 this.configurationFile = configurationFile;
266 lastChanged = -1;
267 }
268 }
269
270 /**
271 * Get the content of logging configuration file.
272 *
273 * @return the content of logging configuration file
274 */
275 public synchronized String getConfiguration() {
276 File file = resolveConfigurationFile();
277 if (file == null || !file.canRead()) {
278 return null;
279 }
280 Reader in = null;
281 try {
282 StringBuffer configuration = new StringBuffer();
283 in = new InputStreamReader(new FileInputStream(file));
284 char[] buffer = new char[4096];
285 for (int size = in.read(buffer); size >= 0; size = in.read(buffer)) {
286 configuration.append(buffer, 0, size);
287 }
288 return configuration.toString();
289 } catch (IOException e) {
290 e.printStackTrace();
291 } finally {
292 if (in != null) {
293 try {
294 in.close();
295 } catch (IOException e1) {
296 e1.printStackTrace();
297 }
298 }
299 }
300 return null;
301 }
302
303 /**
304 * Overwrites the content of logging configuration file.
305 *
306 * @param configuration the new content of logging configuration file
307 */
308 public synchronized void setConfiguration(final String configuration) throws IOException {
309 if (configuration == null || configuration.length() == 0) {
310 throw new IllegalArgumentException("configuration is null or an empty string");
311 }
312
313 File file = resolveConfigurationFile();
314 if (file == null) {
315 throw new IllegalStateException("Configuration file is null");
316 }
317
318
319 if (!file.getParentFile().exists()) {
320 if (!file.getParentFile().mkdirs()) {
321 throw new IllegalStateException("Could not create parent directory of log configuration file: " + file.getParent());
322 }
323 }
324
325
326 if (file.exists() && !file.canWrite()) {
327 throw new IllegalStateException("Configuration file is not writable: " + file.getAbsolutePath());
328 }
329
330 OutputStream out = null;
331 try {
332 out = new FileOutputStream(file);
333 out.write(configuration.getBytes());
334 } finally {
335 if (out != null) {
336 try {
337 out.close();
338 } catch (IOException e) {
339 e.printStackTrace();
340 }
341 }
342 }
343 }
344
345 public synchronized String[] getLogFileNames() {
346 List list = new ArrayList();
347 for (Enumeration e = Logger.getRootLogger().getAllAppenders(); e.hasMoreElements();) {
348 Object appender = e.nextElement();
349 if (appender instanceof FileAppender) {
350 list.add(((FileAppender) appender).getFile());
351 }
352 }
353 return (String[]) list.toArray(new String[list.size()]);
354 }
355
356 private static SearchResults searchFile(File file, String targetLevel, Pattern textSearch, Integer start, Integer stop, int max, boolean stacks) {
357 List list = new LinkedList();
358 boolean capped = false;
359 int lineCount = 0;
360 try {
361 RandomAccessFile raf = new RandomAccessFile(file, "r");
362 FileChannel fc = raf.getChannel();
363 MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
364 CharBuffer cb = Charset.forName("US-ASCII").decode(bb);
365 Matcher target = null;
366 Matcher any = null;
367 Matcher lines = FULL_LINE_PATTERN.matcher(cb);
368 Matcher text = textSearch == null ? null : textSearch.matcher("");
369 boolean hit = false;
370 max = Math.min(max, MAX_SEARCH_RESULTS);
371 while(lines.find()) {
372 ++lineCount;
373 if(target == null) {
374 if(DEFAULT_ANY_START.matcher(cb.subSequence(lines.start(), lines.end())).find()) {
375 target = getDefaultPatternForLevel(targetLevel).matcher("");
376 any = DEFAULT_ANY_START.matcher("");
377 } else {
378 target = getUnknownPatternForLevel(targetLevel).matcher("");
379 any = UNKNOWN_ANY_START.matcher("");
380 }
381 }
382 if(start != null && start.intValue() > lineCount) {
383 continue;
384 }
385 if(stop != null && stop.intValue() < lineCount) {
386 continue;
387 }
388 CharSequence line = cb.subSequence(lines.start(), lines.end());
389 target.reset(line);
390 if(target.find()) {
391 if(text != null) {
392 text.reset(line);
393 if(!text.find()) {
394 hit = false;
395 continue;
396 }
397 }
398 list.add(new LogMessage(lineCount,line.toString()));
399 if(list.size() > max) {
400 list.remove(0);
401 capped = true;
402 }
403 hit = true;
404 } else if(stacks && hit) {
405 any.reset(line);
406 if(!any.find()) {
407 list.add(new LogMessage(lineCount,line.toString()));
408 if(list.size() > max) {
409 list.remove(0);
410 capped = true;
411 }
412 } else {
413 hit = false;
414 }
415 }
416 }
417 fc.close();
418 raf.close();
419 } catch (Exception e) {}
420 return new SearchResults(lineCount, (LogMessage[]) list.toArray(new LogMessage[list.size()]), capped);
421 }
422
423 private static String substituteSystemProps(String source) {
424 StringBuffer buf = new StringBuffer();
425 int last = 0;
426 Matcher m = VARIABLE_PATTERN.matcher(source);
427 while(m.find()) {
428 buf.append(source.substring(last, m.start()));
429 String prop = source.substring(m.start()+2, m.end()-1);
430 buf.append(System.getProperty(prop));
431 last = m.end();
432 }
433 buf.append(source.substring(last));
434 return buf.toString();
435 }
436
437 private static Pattern getDefaultPatternForLevel(String targetLevel) {
438 if(targetLevel.equals("FATAL")) {
439 return DEFAULT_FATAL_START;
440 } else if(targetLevel.equals("ERROR")) {
441 return DEFAULT_ERROR_START;
442 } else if(targetLevel.equals("WARN")) {
443 return DEFAULT_WARN_START;
444 } else if(targetLevel.equals("INFO")) {
445 return DEFAULT_INFO_START;
446 } else if(targetLevel.equals("DEBUG")) {
447 return DEFAULT_DEBUG_START;
448 } else {
449 return DEFAULT_ANY_START;
450 }
451 }
452
453 private static Pattern getUnknownPatternForLevel(String targetLevel) {
454 if(targetLevel.equals("FATAL")) {
455 return UNKNOWN_FATAL_START;
456 } else if(targetLevel.equals("ERROR")) {
457 return UNKNOWN_ERROR_START;
458 } else if(targetLevel.equals("WARN")) {
459 return UNKNOWN_WARN_START;
460 } else if(targetLevel.equals("INFO")) {
461 return UNKNOWN_INFO_START;
462 } else if(targetLevel.equals("DEBUG")) {
463 return UNKNOWN_DEBUG_START;
464 } else {
465 return UNKNOWN_ANY_START;
466 }
467 }
468
469 public SearchResults getMatchingItems(String logFile, Integer firstLine, Integer lastLine, String minLevel, String text, int maxResults, boolean includeStackTraces) {
470
471 if(logFile == null) {
472 throw new IllegalArgumentException("Must specify a log file");
473 }
474 String[] files = getLogFileNames();
475 boolean found = false;
476 for (int i = 0; i < files.length; i++) {
477 if(files[i].equals(logFile)) {
478 found = true;
479 break;
480 }
481 }
482 if(!found) {
483 throw new IllegalArgumentException("Not a log file!");
484 }
485
486 if(minLevel == null) {
487 minLevel = "TRACE";
488 } else if(!minLevel.equals("FATAL") && !minLevel.equals("ERROR") && !minLevel.equals("WARN") &&
489 !minLevel.equals("INFO") && !minLevel.equals("DEBUG") && !minLevel.equals("TRACE")) {
490 throw new IllegalArgumentException("Not a valid log level");
491 }
492
493 Pattern textPattern = null;
494 try {
495 textPattern = text == null || text.equals("") ? null : Pattern.compile(text);
496 } catch (PatternSyntaxException e) {
497 throw new IllegalArgumentException("Bad regular expression '"+text+"'");
498 }
499
500 File log = new File(substituteSystemProps(logFile));
501 if(!log.exists()) {
502 throw new IllegalArgumentException("Log file "+log.getAbsolutePath()+" does not exist");
503 }
504
505 return searchFile(log, minLevel, textPattern, firstLine, lastLine, maxResults, includeStackTraces);
506 }
507
508 /**
509 * Force the logging system to reconfigure.
510 */
511 public void reconfigure() {
512 File file = resolveConfigurationFile();
513 if (file == null || !file.exists()) {
514 return;
515 }
516
517
518 System.setProperty("org.apache.geronimo.log.ConsoleLogLevel", GeronimoLogging.getConsoleLogLevel().toString());
519
520 try {
521 URLConfigurator.configure(file.toURL());
522 } catch (MalformedURLException e) {
523 e.printStackTrace();
524 }
525
526
527 GeronimoLogFactory logFactory = (GeronimoLogFactory) LogFactory.getFactory();
528 Set instances = logFactory.getInstances();
529 for (Iterator iterator = instances.iterator(); iterator.hasNext();) {
530 Object log = iterator.next();
531 if (log instanceof CachingLog4jLog) {
532 ((CachingLog4jLog)log).updateLevelInfo();
533 }
534 }
535 }
536
537 private synchronized void schedule() {
538 if (timer != null) {
539
540 if (monitor != null) {
541 monitor.cancel();
542 }
543
544
545 monitor = new URLMonitorTask();
546 TimerTask task = monitor;
547 timer.schedule(monitor, 1000 * refreshPeriod, 1000 * refreshPeriod);
548 task.run();
549 }
550 }
551
552 public void doStart() {
553 LogFactory logFactory = LogFactory.getFactory();
554 if (logFactory instanceof GeronimoLogFactory) {
555 synchronized (this) {
556 timer = new Timer(true);
557
558
559 schedule();
560
561
562 Logger logger = LogManager.getRootLogger();
563
564 reconfigure();
565
566 File file = resolveConfigurationFile();
567 if (file != null) {
568 lastChanged = file.lastModified();
569 }
570 logEnvInfo(logger);
571 }
572
573
574 GeronimoLogFactory geronimoLogFactory = (GeronimoLogFactory) logFactory;
575 synchronized (geronimoLogFactory) {
576 if (!(geronimoLogFactory.getLogFactory() instanceof CachingLog4jLogFactory)) {
577 geronimoLogFactory.setLogFactory(new CachingLog4jLogFactory());
578 }
579 }
580 }
581
582 synchronized (this) {
583 running = true;
584 }
585 }
586
587 public synchronized void doStop() {
588 running = false;
589 if (monitor != null) {
590 monitor.cancel();
591 monitor = null;
592 }
593 if (timer != null) {
594 timer.cancel();
595 timer = null;
596 }
597 }
598
599 public void doFail() {
600 doStop();
601 }
602
603 private synchronized File resolveConfigurationFile() {
604 try {
605 return serverInfo.resolveServer(configurationFile);
606 } catch (Exception e) {
607 return null;
608 }
609 }
610
611 private void logEnvInfo(Logger log) {
612 try {
613 log.info("----------------------------------------------");
614 log.info("Started Logging Service");
615 log.debug("Log4jService created with configFileName=" + this.configurationFile +
616 ", refreshPeriodSeconds=" + this.refreshPeriod);
617 log.info("Runtime Information:");
618 log.info(" Install Directory = " + DirectoryUtils.getGeronimoInstallDirectory().toString());
619 log.info(" JVM in use = " + System.getProperty("java.vendor") + " Java " + System.getProperty("java.version"));
620 log.info("Java Information:");
621 log.info(" System property [java.runtime.name] = " + System.getProperty("java.runtime.name"));
622 log.info(" System property [java.runtime.version] = " + System.getProperty("java.runtime.version"));
623 log.info(" System property [os.name] = " + System.getProperty("os.name"));
624 log.info(" System property [os.version] = " + System.getProperty("os.version"));
625 log.info(" System property [sun.os.patch.level] = " + System.getProperty("sun.os.patch.level"));
626 log.info(" System property [os.arch] = " + System.getProperty("os.arch"));
627 log.info(" System property [java.class.version] = " + System.getProperty("java.class.version"));
628 log.info(" System property [locale] = " + System.getProperty("user.language") + "_" + System.getProperty("user.country"));
629 log.info(" System property [unicode.encoding] = " + System.getProperty("sun.io.unicode.encoding"));
630 log.info(" System property [file.encoding] = " + System.getProperty("file.encoding"));
631 log.info(" System property [java.vm.name] = " + System.getProperty("java.vm.name"));
632 log.info(" System property [java.vm.vendor] = " + System.getProperty("java.vm.vendor"));
633 log.info(" System property [java.vm.version] = " + System.getProperty("java.vm.version"));
634 log.info(" System property [java.vm.info] = " + System.getProperty("java.vm.info"));
635 log.info(" System property [java.home] = " + System.getProperty("java.home"));
636 log.info(" System property [java.classpath] = " + System.getProperty("java.classpath"));
637 log.info(" System property [java.library.path] = " + System.getProperty("java.library.path"));
638 log.info(" System property [java.endorsed.dirs] = " + System.getProperty("java.endorsed.dirs"));
639 log.info(" System property [java.ext.dirs] = " + System.getProperty("java.ext.dirs"));
640 log.info(" System property [sun.boot.class.path] = " + System.getProperty("sun.boot.class.path"));
641 log.info("----------------------------------------------");
642 } catch (Exception e) {
643 String msg = "Exception caught during logging of Runtime Information. Exception=" + e.toString();
644 log.error(msg);
645 System.err.println(msg);
646 }
647 }
648
649
650 private class URLMonitorTask extends TimerTask {
651 public void run() {
652 try {
653 long lastModified;
654 synchronized (this) {
655 if (running == false) {
656 return;
657 }
658
659 File file = resolveConfigurationFile();
660 if (file == null) {
661 return;
662 }
663
664 lastModified = file.lastModified();
665 }
666
667 if (lastChanged < lastModified) {
668 lastChanged = lastModified;
669 reconfigure();
670 }
671 } catch (Exception e) {
672 }
673 }
674 }
675
676 private static class CachingLog4jLogFactory extends LogFactory {
677 public Log getInstance(Class clazz) throws LogConfigurationException {
678 return getInstance(clazz.getName());
679 }
680
681 public Log getInstance(String name) throws LogConfigurationException {
682 return new CachingLog4jLog(name);
683 }
684
685 public Object getAttribute(String name) {
686 return null;
687 }
688
689 public String[] getAttributeNames() {
690 return new String[0];
691 }
692
693 public void release() {
694 }
695
696 public void removeAttribute(String name) {
697 }
698
699 public void setAttribute(String name, Object value) {
700 }
701 }
702
703 public static final GBeanInfo GBEAN_INFO;
704
705 static {
706 GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic(Log4jService.class, "SystemLog");
707
708 infoFactory.addAttribute("configFileName", String.class, true);
709 infoFactory.addAttribute("refreshPeriodSeconds", int.class, true);
710 infoFactory.addAttribute("configuration", String.class, false);
711 infoFactory.addAttribute("rootLoggerLevel", String.class, false);
712
713 infoFactory.addReference("ServerInfo", ServerInfo.class, "GBean");
714
715 infoFactory.addOperation("reconfigure");
716 infoFactory.addOperation("setLoggerLevel", new Class[]{String.class, String.class});
717 infoFactory.addOperation("getLoggerLevel", new Class[]{String.class});
718 infoFactory.addOperation("getLoggerEffectiveLevel", new Class[]{String.class});
719 infoFactory.addInterface(SystemLog.class);
720
721 infoFactory.setConstructor(new String[]{"configFileName", "refreshPeriodSeconds", "ServerInfo"});
722
723 GBEAN_INFO = infoFactory.getBeanInfo();
724 }
725
726 public static GBeanInfo getGBeanInfo() {
727 return GBEAN_INFO;
728 }
729 }